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

 @(#) m2pa_sl.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:41

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

 Copyright (c) 2001-2002  OpenSS7 Corporation <http://www.openss7.com>
 Copyright (c) 1997-2000  Brian F. G. Bidulock <bidulock@dallas.net>

 All Rights Reserved.

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

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

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

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

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

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

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

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

 Last Modified 2002/04/13 19:27:41 by brian

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

#ident "@(#) m2pa_sl.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:41"

static char const ident[] = "m2pa_sl.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:41";

#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 "../lock.h"
#include "../debug.h"
#include "../bufq.h"

#define M2PA_DESCRIP	"M2PA/SCTP SIGNALLING LINK (SL) STREAMS MODULE."
#define M2PA_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  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 int
#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_rput(queue_t *, mblk_t *);
STATIC INT sl_rsrv(queue_t *);

STATIC struct qinit sl_rinit = {
	sl_rput,		/* Read put (msg from below) */
	sl_rsrv,		/* Read queue service */
	sl_open,		/* Each open */
	sl_close,		/* Last close */
	NULL,			/* Admin (not used) */
	&sl_minfo,		/* Information */
	NULL			/* Statistics */
};

STATIC INT sl_wput(queue_t *, mblk_t *);
STATIC INT sl_wsrv(queue_t *);

STATIC struct qinit sl_winit = {
	sl_wput,		/* Write put (msg from above) */
	sl_wsrv,		/* Write queue service */
	NULL,			/* Each open */
	NULL,			/* Last close */
	NULL,			/* Admin (not used) */
	&sl_minfo,		/* Information */
	NULL			/* Statistics */
};

STATIC struct streamtab sl_info = {
	&sl_rinit,		/* Upper read queue */
	&sl_winit,		/* Upper write queue */
	NULL,			/* Lower read queue */
	NULL			/* Lower write queue */
};

#define QR_DONE		0
#define QR_ABSORBED		1
#define QR_TRIMMED		2
#define QR_LOOP		3
#define QR_PASSALONG		4
#define QR_PASSFLOW		5
#define QR_DISABLE		6

/* 
 *  =========================================================================
 *
 *  M2PA-SL Private Structure
 *
 *  =========================================================================
 */
typedef struct sl {
	struct sl *next;
	queue_t *rq;
	queue_t *wq;

	lis_spin_lock_t lock;	/* queue lock */
	void * users;		/* users of this 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 */

	mblk_t *rbuf;		/* reassembly buffer */

	uint state;		/* link state */

	uint rbid;		/* rd bufcall id */
	uint wbid;		/* wr bufcall id */

	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 bsnr;		/* received msu seq number */
	uint fsnx;		/* expected msu seq number */
	uint fsnt;		/* transmitted msu seq number */
	uint back;		/* bad acks */
	uint bbsn;		/* bad bsn */
	uint bfsn;		/* bad fsn */

	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	0x30
#define M2PA_VERSION_DRAFT3_1	0x31
#define M2PA_VERSION_DRAFT4	0x40
#define M2PA_VERSION_DRAFT4_1	0x41
#define M2PA_VERSION_DRAFT4_9	0x49
#define M2PA_VERSION_DEFAULT	M2PA_VERSION_DRAFT4_1

/* 
 *  =========================================================================
 *
 *  Locking
 *
 *  =========================================================================
 */
#define SL_PRIV(__q) ((sl_t *)(__q)->q_ptr)
#define sl_init_lock(__sl) lis_spin_lock_init(&(__sl)->lock,"m2pa-sl-private")
#define sl_locked(__sl) ((__sl)->users)

STATIC INLINE int sl_trylock(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	lis_spin_lock_t *lock = &sl->lock;
	lis_spin_lock_bh(lock);
	if (sl->users && sl->users != current) {
		lis_spin_unlock_bh(lock);
		return (!0);
	}
	sl->users = current;
	lis_spin_unlock_bh(lock);
	return (0);
}
STATIC INLINE int sl_rx_wakeup(queue_t * q);
STATIC INLINE int sl_tx_wakeup(queue_t * q);
STATIC INLINE void sl_unlock(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	lis_spin_lock_t *lock = &sl->lock;
	sl_rx_wakeup(q);
	sl_tx_wakeup(q);
	lis_spin_lock_bh(lock);
	if (sl->users && sl->users == current)
		sl->users = NULL;
	else
		ptrace(("Double unlock\n"));
	lis_spin_unlock_bh(lock);
}

/* 
 *  =========================================================================
 *
 *  Buffet Allocation
 *
 *  =========================================================================
 */
/* 
 *  BUFSRV calls service routine
 *  -------------------------------------------------------------------------
 */
STATIC void sl_bufsrv(long data)
{
	queue_t *q = (queue_t *) data;
	if (q) {
		sl_t *sl = SL_PRIV(q);
		if (q == sl->rq)
			if (sl->rbid)
				sl->rbid = 0;
		if (q == sl->wq)
			if (sl->wbid)
				sl->wbid = 0;
		qenable(q);
	}
}

/* 
 *  ALLOCB
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *sl_allocb(queue_t * q, size_t size, int prior)
{
	mblk_t *mp;
	if ((mp = allocb(size, prior)))
		return (mp);
	{
		sl_t *sl = SL_PRIV(q);
		if (q == sl->rq) {
			if (!sl->rbid)
				sl->rbid = bufcall(size, prior, &sl_bufsrv, (long) q);
			return (NULL);
		}
		if (q == sl->wq) {
			if (!sl->wbid)
				sl->wbid = bufcall(size, prior, &sl_bufsrv, (long) q);
			return (NULL);
		}
	}
	swerr();
	return (NULL);
}

/* 
 *  ESBALLOC
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *sl_esballoc(queue_t * q, unsigned char *base, size_t size, int prior, frtn_t * frtn)
{
	mblk_t *mp;
	if ((mp = esballoc(base, size, prior, frtn)))
		return (mp);
	{
		sl_t *sl = SL_PRIV(q);
		if (q == sl->rq) {
			if (!sl->rbid)
				sl->rbid = esbbcall(prior, &sl_bufsrv, (long) q);
			return (NULL);
		}
		if (q == sl->wq) {
			if (!sl->wbid)
				sl->wbid = esbbcall(prior, &sl_bufsrv, (long) q);
			return (NULL);
		}
	}
	swerr();
	return (NULL);
}

/* 
 *  =========================================================================
 *
 *  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_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))
#define	M2PA_STATUS_OUT_OF_SERVICE		(__constant_htonl(9))
#define M2PA_STATUS_ACK				(__constant_htonl(10))

#ifndef PAD4
#define PAD4(__x) (((__x)+0x3)&~0x3)
#endif

/* 
 *  =========================================================================
 *
 *  OUTPUT Events
 *
 *  =========================================================================
 *  -------------------------------------------------------------------------
 *
 *  SL Provider (M2PA) -> SL User Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  LMI_INFO_ACK
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_info_ack(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_info_ack_t *p;
	if ((mp = sl_allocb(q, 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);
	}
	return (-ENOBUFS);
}

#if 0
/* 
 *  LMI_OK_ACK
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_ok_ack(queue_t * q, long prim)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ((mp = sl_allocb(q, 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);
	}
	return (-ENOBUFS);
}
#endif
/* 
 *  LMI_ERROR_ACK
 *  ---------------------------------------------
 */
STATIC int lmi_error_ack(queue_t * q, long prim, long err)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_error_ack_t *p;
	switch (err) {
	case QR_DONE:
	case QR_ABSORBED:
	case QR_TRIMMED:
	case QR_LOOP:
	case QR_PASSALONG:
	case QR_PASSFLOW:
	case QR_DISABLE:
		ptrace(("WARNING: Shouldn't pass Q returns to m_error_reply\n"));
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		return (err);
	}
	if ((mp = sl_allocb(q, 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);
	}
	return (-ENOBUFS);
}

/* 
 *  LMI_ENABLE_CON
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_enable_con(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_enable_con_t *p;
	ensure(sl->i_state == LMI_ENABLE_PENDING, return (-EFAULT));
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

/* 
 *  LMI_DISABLE_CON
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_disable_con(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_disable_con_t *p;
	ensure(sl->i_state == LMI_DISABLE_PENDING, return (-EFAULT));
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

/* 
 *  LMI_OPTMGMT_ACK
 *  ---------------------------------------------
 */
#if 0
STATIC INLINE int lmi_optmgmt_ack(queue_t * q, ulong flags, void *opt_ptr, size_t opt_len)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	if ((mp = sl_allocb(q, 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);
	}
	return (-ENOBUFS);
}
#endif
/* 
 *  LMI_ERROR_IND
 *  ---------------------------------------------
 */
#if 0
STATIC INLINE int lmi_error_ind(queue_t * q, long err)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ((mp = sl_allocb(q, 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);
	}
	return (-ENOBUFS);
}
#endif
/* 
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
#if 0
STATIC INLINE int lmi_stats_ind(queue_t * q, ulong interval, ulong timestamp)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_stats_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}
#endif
/* 
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
STATIC int lmi_event_ind(queue_t * q, ulong oid, ulong severity, void *inf_ptr, size_t inf_len)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_event_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_PDU_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_pdu_ind(queue_t * q, mblk_t * dp)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_pdu_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_LINK_CONGESTED_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_link_congested_ind(queue_t * q, ulong cong, ulong disc)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_link_cong_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_LINK_CONGESTION_CEASED_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_link_congestion_ceased_ind(queue_t * q, ulong cong, ulong disc)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_link_cong_ceased_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_RETRIEVED_MESSAGE_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_retrieved_message_ind(queue_t * q, mblk_t * dp)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_retrieved_msg_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_RETRIEVAL_COMPLETE_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_retrieval_complete_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_retrieval_comp_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_RB_CLEARED_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_rb_cleared_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_rb_cleared_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_BSNT_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_bsnt_ind(queue_t * q, ulong bsnt)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_bsnt_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_IN_SERVICE_IND
 *  ---------------------------------------------
 */
STATIC int sl_in_service_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_in_service_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_OUT_OF_SERVICE_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_out_of_service_ind(queue_t * q, ulong reason)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_out_of_service_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_REMOTE_PROCESSOR_OUTAGE_IND
 *  ---------------------------------------------
 */
STATIC int sl_remote_processor_outage_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_rem_proc_out_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_REMOTE_PROCESSOR_RECOVERED_IND
 *  ---------------------------------------------
 */
STATIC int sl_remote_processor_recovered_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_rem_proc_recovered_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_RTB_CLEARED_IND
 *  ---------------------------------------------
 */
STATIC int sl_rtb_cleared_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_rtb_cleared_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_RETRIEVAL_NOT_POSSIBLE_IND
 *  ---------------------------------------------
 */
STATIC INLINE int sl_retrieval_not_possible_ind(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_retrieval_not_poss_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  SL_BSNT_NOT_RETRIEVABLE_IND
 *  ---------------------------------------------
 */
#if 0
STATIC INLINE int sl_bsnt_not_retrievable_ind(queue_t * q, ulong bsnt)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_bsnt_not_retr_ind_t *p;
	if (canputnext(sl->rq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}
#endif
/* 
 *  -------------------------------------------------------------------------
 *
 *  NPI User (M2PA) -> NPI Provider (SCTP) Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  N_DATA_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int n_data_req(queue_t * q, ulong flags, void *qos_ptr, size_t qos_len, mblk_t * dp)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	N_data_req_t *p;
	if (canputnext(sl->wq)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

/* 
 *  N_EXDATA_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int n_exdata_req(queue_t * q, void *qos_ptr, size_t qos_len, mblk_t * dp)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	N_exdata_req_t *p;
	if (bcanputnext(sl->wq, 1)) {
		if ((mp = sl_allocb(q, 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);
		}
		return (-ENOBUFS);
	}
	return (-EBUSY);
}

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

/* 
 *  -------------------------------------------------------------------------
 *
 *  SL Peer (M2PA) -> SL Peer Sent Messages
 *
 *  -------------------------------------------------------------------------
 *
 *  M2PA SEND PROVING
 *  ---------------------------------------------
 */
STATIC INLINE int sl_send_proving(queue_t * q, size_t plen)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		if ((mp = sl_allocb(q, 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)++ = 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(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4:
	case M2PA_VERSION_DRAFT4_1:
		/*
		 *  draft-ietf-sigtran-m2pa-04.txt does not have a separate
		 *  Proving message: it uses the status proving message for
		 *  sending proving data will fill bytes.
		 */
		if ((mp = sl_allocb(q, 4 * sizeof(uint32_t) + plen, BPRI_MED))) {
			mp->b_datap->db_type = M_DATA;
			*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_MESSAGE;
			*((uint32_t *) mp->b_wptr)++ = htonl(4 * sizeof(uint32_t) + plen);
			*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnr);
			*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnt);
			if (sl->flags & MF_LOC_EMERG)
				*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_PROVING_EMERGENCY;
			else
				*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_PROVING_NORMAL;
			fixme(("We should include a test pattern.\n"));
			bzero(mp->b_wptr, plen);
			mp->b_wptr += plen;
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	default:
	case M2PA_VERSION_DRAFT4_9:
		if ((mp = sl_allocb(q, 5 * sizeof(uint32_t) + plen, BPRI_MED))) {
			mp->b_datap->db_type = M_DATA;
			*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_MESSAGE;
			*((uint32_t *) mp->b_wptr)++ = htonl(5 * sizeof(uint32_t) + plen);
			*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnr);
			*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnt);
			if (sl->flags & MF_LOC_EMERG)
				*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_PROVING_EMERGENCY;
			else
				*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_PROVING_NORMAL;
			fixme(("We should include a test pattern.\n"));
			bzero(mp->b_wptr, plen);
			mp->b_wptr += plen;
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	}
}

/* 
 *  M2PA SEND STATUS
 *  ---------------------------------------------
 */
STATIC int sl_send_status(queue_t * q, uint32_t status)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_STATUS_STREAM, };
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		if ((mp = sl_allocb(q, 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(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
		/*
		 *  draft-ietf-sigtran-m2pa-04.txt has sequence numbers in an
		 *  M2PA-specific header.
		 */
		if ((mp = sl_allocb(q, 4 * 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(4 * sizeof(uint32_t));
			*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnr);
			*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnt);
			*((uint32_t *) mp->b_wptr)++ = status;
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4_9:
		if ((mp = sl_allocb(q, 5 * 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(5 * sizeof(uint32_t));
			*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnr);
			*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnt);
			*((uint32_t *) mp->b_wptr)++ = status;
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	}
}

/* 
 *  M2PA SEND ACK
 *  ---------------------------------------------
 */
STATIC INLINE int sl_send_ack(queue_t * q, ulong count)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	if (!count)
		return (0);
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
		/*
		 *  draft-ietf-sigtran-m2pa-03.txt does not have a separate
		 *  acknowledgement message.  Acknowledgement is done at the
		 *  SCTP level only.
		 */
		return (0);
	case M2PA_VERSION_DRAFT3_1:
		if ((mp = sl_allocb(q, 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(q, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4:
		/*
		 *  draft-ietf-sigtran-m2pa-04.txt again does not have a
		 *  separate acknowledgement message.  There is no way to
		 *  acknowledge data without sending status or data in
		 *  M2PA-04.
		 */
		return (0);
	default:
	case M2PA_VERSION_DRAFT4_1:
		/*
		 *  This extension uses the otherwise useless "In Service"
		 *  state message from draft-ietf-sigtran-m2pa-04.txt as an
		 *  acknowledgement message (like a FISU).  These messages are
		 *  sent expedited to speed them up, but that means they may
		 *  arrive out of order and, therefore, some sequence numbers
		 *  should be ignored by the receiver.
		 */
		if ((mp = sl_allocb(q, 4 * 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(4 * sizeof(uint32_t));
			*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnr);
			*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnt);
			*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_ACK;
			if (!(err = n_exdata_req(q, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4_9:
		/*
		 *  This is the same as extension 4.1, except that it uses the
		 *  larger 32-bit sequence numbers expected to appear in
		 *  draft-ietf-sigtran-m2pa-05.txt.
		 */
		if ((mp = sl_allocb(q, 5 * 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(5 * sizeof(uint32_t));
			*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnr);
			*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnt);
			*((uint32_t *) mp->b_wptr)++ = M2PA_STATUS_ACK;
			if (!(err = n_exdata_req(q, &qos, sizeof(qos), mp)))
				return (0);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	}
}

/* 
 *  M2PA SEND DATA
 *  ---------------------------------------------
 */
STATIC INLINE int sl_send_data(queue_t * q, mblk_t * dp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *mp, *db;
	ulong rcpt = sl->version < M2PA_VERSION_DRAFT3_1 ? 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))) {
		switch (sl->version) {
		case M2PA_VERSION_DRAFT3:
		case M2PA_VERSION_DRAFT3_1:
			if ((mp = sl_allocb(q, 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)++ = htonl(dlen + 2 * sizeof(uint32_t));
				mp->b_cont = db;
				pullupmsg(mp, -1);
				if (!(err = n_data_req(q, rcpt, &qos, sizeof(qos), mp)))
					return (0);
				freemsg(mp);
				return (err);
			}
			break;
		case M2PA_VERSION_DRAFT4:
		case M2PA_VERSION_DRAFT4_1:
			if ((mp = sl_allocb(q, 3 * 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)++ = htonl(dlen + 3 * sizeof(uint32_t));
				*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnr);
				*((uint16_t *) mp->b_wptr)++ = htons(sl->fsnt + 1);
				mp->b_cont = db;
				pullupmsg(mp, -1);
				if (!(err = n_data_req(q, rcpt, &qos, sizeof(qos), mp)))
					return (0);
				freemsg(mp);
				return (err);
			}
			break;
		case M2PA_VERSION_DRAFT4_9:
			if ((mp = sl_allocb(q, 4 * 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)++ = htonl(dlen + 4 * sizeof(uint32_t));
				*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnr);
				*((uint32_t *) mp->b_wptr)++ = htonl(sl->fsnt + 1);
				mp->b_cont = db;
				pullupmsg(mp, -1);
				if (!(err = n_data_req(q, rcpt, &qos, sizeof(qos), mp)))
					return (0);
				freemsg(mp);
				return (err);
			}
			break;
		}
		freemsg(db);
	}
	return (-ENOBUFS);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Timers
 *
 *  -------------------------------------------------------------------------
 */
enum { t1 = 1, t2, t3, t4, t5, t6, t7, t8, t9 };
STATIC void sl_t1_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t1, 0))
		return;
	printd(("T1 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t1);
}
STATIC void sl_t2_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t2, 0))
		return;
	printd(("T2 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t2);
}
STATIC void sl_t3_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t3, 0))
		return;
	printd(("T3 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t3);
}
STATIC void sl_t4_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t4, 0))
		return;
	printd(("T4 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t4);
}

#if 0
STATIC void sl_t5_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t5, 0))
		return;
	printd(("T5 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t5);
}
#endif
STATIC void sl_t6_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t6, 0))
		return;
	printd(("T6 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t6);
}
STATIC void sl_t7_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t7, 0))
		return;
	printd(("T7 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t7);
}

#if 0
STATIC void sl_t8_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t8, 0))
		return;
	printd(("T8 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t8);
}
STATIC void sl_t9_timeout(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (!xchg(&sl->timer_t9, 0))
		return;
	printd(("T9 Timeout\n"));
	putctl1(sl->rq, M_PCRSE, t9);
}
#endif
STATIC INLINE void sl_timer_stop(queue_t * q, uint t)
{
	sl_t *sl = SL_PRIV(q);
	switch (t) {
	case t1:
		if (sl->timer_t1) {
			printd(("Stopping T1\n"));
			untimeout(xchg(&sl->timer_t1, 0));
		}
		break;
	case t2:
		if (sl->timer_t2) {
			printd(("Stopping T2\n"));
			untimeout(xchg(&sl->timer_t2, 0));
		}
		break;
	case t3:
		if (sl->timer_t3) {
			printd(("Stopping T3\n"));
			untimeout(xchg(&sl->timer_t3, 0));
		}
		break;
	case t4:
		if (sl->timer_t4) {
			printd(("Stopping T4\n"));
			untimeout(xchg(&sl->timer_t4, 0));
		}
		break;
#if 0
	case t5:
		if (sl->timer_t5) {
			printd(("Stopping T5\n"));
			untimeout(xchg(&sl->timer_t5, 0));
		}
		break;
#endif
	case t6:
		if (sl->timer_t6) {
			printd(("Stopping T6\n"));
			untimeout(xchg(&sl->timer_t6, 0));
		}
		break;
	case t7:
		if (sl->timer_t7) {
			printd(("Stopping T7\n"));
			untimeout(xchg(&sl->timer_t7, 0));
		}
		break;
#if 0
	case t8:
		if (sl->timer_t8) {
			printd(("Stopping T8\n"));
			untimeout(xchg(&sl->timer_t8, 0));
		}
		break;
	case t9:
		if (sl->timer_t9) {
			printd(("Stopping T9\n"));
			untimeout(xchg(&sl->timer_t9, 0));
		}
		break;
#endif
	}
}
STATIC INLINE void sl_timer_start(queue_t * q, uint t)
{
	sl_t *sl = SL_PRIV(q);
	spin_lock_bh(&sl->lock);
	switch (t) {
	case t1:
		sl_timer_stop(q, t);
		printd(("Starting T1\n"));
		sl->timer_t1 = timeout(&sl_t1_timeout, (caddr_t) sl, sl->config.t1);
		break;
	case t2:
		sl_timer_stop(q, t);
		printd(("Starting T2\n"));
		sl->timer_t2 = timeout(&sl_t2_timeout, (caddr_t) sl, sl->config.t2);
		break;
	case t3:
		sl_timer_stop(q, t);
		printd(("Starting T3\n"));
		sl->timer_t3 = timeout(&sl_t3_timeout, (caddr_t) sl, sl->config.t3);
		break;
	case t4:
		sl_timer_stop(q, t);
		if (sl->flags & (MF_LOC_EMERG | MF_REM_EMERG)) {
			printd(("Starting T4e\n"));
			sl->timer_t4 = timeout(&sl_t4_timeout, (caddr_t) sl, sl->config.t4e);
		} else {
			printd(("Starting T4n\n"));
			sl->timer_t4 = timeout(&sl_t4_timeout, (caddr_t) sl, sl->config.t4n);
		}
		break;
#if 0
	case t5:
		sl_timer_stop(q, t);
		printd(("Starting T5\n"));
		sl->timer_t5 = timeout(&sl_t5_timeout, (caddr_t) sl, sl->config.t5);
		break;
#endif
	case t6:
		sl_timer_stop(q, t);
		printd(("Starting T6\n"));
		sl->timer_t6 = timeout(&sl_t6_timeout, (caddr_t) sl, sl->config.t6);
		break;
	case t7:
		sl_timer_stop(q, t);
		printd(("Starting T7\n"));
		sl->timer_t7 = timeout(&sl_t7_timeout, (caddr_t) sl, sl->config.t7);
		break;
#if 0
	case t8:
		sl_timer_stop(q, t);
		printd(("Starting T8\n"));
		sl->timer_t8 = timeout(&sl_t8_timeout, (caddr_t) sl, 1);
		break;
	case t9:
		sl_timer_stop(q, t);
		printd(("Starting T9\n"));
		sl->timer_t9 = timeout(&sl_t9_timeout, (caddr_t) sl, 1);
		break;
#endif
	}
	spin_unlock_bh(&sl->lock);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Error Monitoring
 *
 *  -------------------------------------------------------------------------
 */
STATIC INLINE void sl_suerm_start(queue_t * q)
{
	/* SCTP does this for us */
	return;
}
STATIC INLINE void sl_suerm_stop(queue_t * q)
{
	/* SCTP does this for us */
	return;
}
STATIC INLINE void sl_aerm_start(queue_t * q)
{
	(void) sl_send_proving;
	printd(("Start Proving...\n"));
	return;
}
STATIC INLINE void sl_aerm_stop(queue_t * q)
{
	printd(("Stop Proving...\n"));
	return;
}
STATIC int sl_lsc_out_of_service(queue_t * q, ulong reason);
#if 0
STATIC INLINE int sl_iac_abort_proving(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state == MS_PROVING) {
		sl->statem.Cp++;
		sl_timer_stop(q, t4);
		sl_aerm_stop(q);
		if (sl->statem.Cp == sl->config.M) {
			if (sl->notify.events & SL_EVT_FAIL_ALIGNMENT)
				if ((err = lmi_event_ind(q, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)))
					return (err);
			if ((err = sl_lsc_out_of_service(q, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)))
				return (err);
			ptrace(("Link failed: Alignement not possible\n"));
			sl->stats.sl_fail_align_or_proving++;
			return (0);
		}
		sl_timer_start(q, t4);
		sl_aerm_start(q);
		return (0);
	}
	ptrace(("Received signal iac-abort-proving in unexpected state %u.\n", sl->state));
	return (-EFAULT);
}
#endif
#if 0
STATIC INLINE int sl_t5_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	return (-EOPNOTSUPP);
}
STATIC INLINE int sl_t8_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	return (-EOPNOTSUPP);
}
STATIC INLINE int sl_t9_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	(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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state != MS_OUT_OF_SERVICE) {
		if ((err = sl_send_status(q, M2PA_STATUS_OUT_OF_SERVICE)))
			return (err);
		sl_timer_stop(q, t1);
		sl_timer_stop(q, t2);
		sl_timer_stop(q, t3);
		sl_timer_stop(q, t4);
		sl_timer_stop(q, t6);
		sl_timer_stop(q, t7);
		sl_aerm_stop(q);
		sl_suerm_stop(q);
		sl->flags &= ~MF_LPO;
		if (sl->flags & MF_RPO) {
			if (sl->notify.events & SL_EVT_RPO_END)
				if ((err = lmi_event_ind(q, SL_EVT_RPO_END, 0, NULL, 0)))
					return (err);
			ptrace(("RPO Ends\n"));
			sl_rpr_stats(q);
			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(q);
		sl->state = MS_OUT_OF_SERVICE;
		return (0);
	}
	return (0);
}
STATIC int sl_lsc_out_of_service(queue_t * q, ulong reason)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state != MS_OUT_OF_SERVICE) {
		if ((err = sl_out_of_service_ind(q, reason)))
			return (err);
		if ((err = sl_lsc_stop(q)))
			return (err);
		sl->stamp.sl_dur_unavail_failed = jiffies;
		return (0);
	}
	return (0);
}
STATIC INLINE int sl_lsc_power_on(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state == MS_POWER_OFF) {
		sl->flags = 0;
		if ((err = sl_send_status(q, 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 INLINE int sl_lsc_start(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state == MS_OUT_OF_SERVICE) {
		sl->rmsu = 0;
		sl->rack = 0;
		sl->fsnr = 0;
		sl->bsnr = 0;
		sl->fsnx = 1;
		sl->tmsu = 0;
		sl->tack = 0;
		sl->fsnt = 0;
		sl->back = 0;
		sl->bbsn = 0;
		sl->bfsn = 0;
		// sl->flags = 0;
		if ((err = sl_send_status(q, M2PA_STATUS_ALIGNMENT)))
			return (err);
		sl_timer_start(q, t2);
		sl->state = MS_NOT_ALIGNED;
		return (0);
	}
	ptrace(("Received primitive SL_START_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_t2_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state == MS_NOT_ALIGNED) {
		if (sl->notify.events & SL_EVT_FAIL_ALIGNMENT)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)))
			return (err);
		ptrace(("Link failed: Alignment not possible\n"));
		sl->stats.sl_fail_align_or_proving++;
		return (0);
	}
	return (0);
}
STATIC INLINE int sl_lsc_status_alignment(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, M2PA_STATUS_PROVING_EMERGENCY);
		else
			err = sl_send_status(q, M2PA_STATUS_PROVING_NORMAL);
		if (err)
			return (err);
		sl_timer_stop(q, t2);
		sl_timer_start(q, t3);
		sl->state = MS_ALIGNED;
		return (0);
	case MS_ALIGNED:
		return (0);
	case MS_PROVING:
		sl_aerm_stop(q);
		sl_timer_stop(q, t4);
		sl_timer_start(q, t3);
		sl->state = MS_ALIGNED;
		return (0);
	default:
		if (sl->notify.events & SL_EVT_FAIL_RECEIVED_SIO)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_RECEIVED_SIO, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_RECEIVED_SIO)))
			return (err);
		ptrace(("Link failed: Received SIO\n"));
		return (0);
	}
}
STATIC INLINE int sl_lsc_emergency(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	sl->flags |= MF_LOC_EMERG;
	switch (sl->state) {
	case MS_PROVING:
		if ((err = sl_send_status(q, M2PA_STATUS_PROVING_EMERGENCY)))
			return (err);
		sl_timer_stop(q, t4);
		sl_aerm_stop(q);
		sl_timer_start(q, t4);
		sl_aerm_start(q);
		return (0);
	case MS_ALIGNED:
		if ((err = sl_send_status(q, M2PA_STATUS_PROVING_EMERGENCY)))
			return (err);
		return (0);
	}
	ptrace(("Received primitive SL_EMERGENCY_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_emergency_ceases(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	sl->flags &= ~MF_LOC_EMERG;
	return (0);
}
STATIC INLINE int sl_lsc_status_proving_normal(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	switch (sl->state) {
	case MS_NOT_ALIGNED:
		if (sl->flags & MF_LOC_EMERG)
			err = sl_send_status(q, M2PA_STATUS_PROVING_EMERGENCY);
		else
			err = sl_send_status(q, M2PA_STATUS_PROVING_NORMAL);
		if (err)
			return (err);
		sl_timer_stop(q, t2);
		sl_timer_start(q, t3);
		sl->state = MS_ALIGNED;
		/* fall thru */
	case MS_ALIGNED:
		sl->flags &= ~MF_REM_EMERG;
		sl_timer_stop(q, t3);
		sl_aerm_start(q);
		sl_timer_start(q, t4);
		sl->statem.Cp = 0;
		sl->state = MS_PROVING;
		return (0);
	case MS_IN_SERVICE:
	case MS_PROCESSOR_OUTAGE:
		if (sl->notify.events & SL_EVT_FAIL_RECEIVED_SIN)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_RECEIVED_SIN, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_RECEIVED_SIN)))
			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 INLINE int sl_lsc_status_proving_emergency(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	switch (sl->state) {
	case MS_NOT_ALIGNED:
		sl_timer_stop(q, t2);
		if (sl->flags & MF_LOC_EMERG)
			err = sl_send_status(q, M2PA_STATUS_PROVING_EMERGENCY);
		else
			err = sl_send_status(q, M2PA_STATUS_PROVING_NORMAL);
		if (err)
			return (err);
		sl_timer_start(q, t3);
		sl->state = MS_ALIGNED;
		/* fall thru */
	case MS_ALIGNED:
		sl->flags |= MF_REM_EMERG;
		sl_timer_stop(q, t3);
		sl_aerm_start(q);
		sl_timer_start(q, t4);
		sl->statem.Cp = 0;
		sl->state = MS_PROVING;
		return (0);
	case MS_PROVING:
		sl->flags |= MF_REM_EMERG;
		sl_timer_stop(q, t4);
		sl_aerm_stop(q);
		sl_timer_start(q, t4);
		sl_aerm_start(q);
		return (0);
	case MS_IN_SERVICE:
	case MS_PROCESSOR_OUTAGE:
		if (sl->notify.events & SL_EVT_FAIL_RECEIVED_SIE)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_RECEIVED_SIE, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_RECEIVED_SIE)))
			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 INLINE int sl_t1_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->notify.events & SL_EVT_FAIL_T1_TIMEOUT)
		if ((err = lmi_event_ind(q, SL_EVT_FAIL_T1_TIMEOUT, 0, NULL, 0)))
			return (err);
	if ((err = sl_lsc_out_of_service(q, SL_FAIL_T1_TIMEOUT)))
		return (err);
	ptrace(("Link failed: T1 Timeout\n"));
	sl->stats.sl_fail_align_or_proving++;
	return (0);
}
STATIC INLINE int sl_t3_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state == MS_ALIGNED) {
		if (sl->notify.events & SL_EVT_FAIL_ALIGNMENT)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)))
			return (err);
		ptrace(("Link failed: T3 Timeout\n"));
		sl->stats.sl_fail_align_or_proving++;
		return (0);
	}
	return (0);
}
STATIC INLINE int sl_t4_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->state == MS_PROVING) {
		sl_aerm_stop(q);
		sl->flags &= ~MF_LOC_EMERG;
		sl->flags &= ~MF_REM_EMERG;
		sl_suerm_start(q);
		sl->flags |= MF_LOC_INS;
		if (sl->flags & MF_LPO) {
			if ((err = sl_send_status(q, M2PA_STATUS_PROCESSOR_OUTAGE)))
				return (err);
			if (sl->flags & MF_REM_INS) {
				if ((err = sl_in_service_ind(q)))
					return (err);
				sl->flags &= ~MF_SEND_MSU;
				sl->flags &= ~MF_RECV_MSU;
				sl_oos_stats(q);
				sl->state = MS_PROCESSOR_OUTAGE;
			} else {
				sl_timer_start(q, t1);
				sl->state = MS_ALIGNED_NOT_READY;
			}
		} else {
			mblk_t *mp;
			if ((mp = sl->tb.q_head)) {
				if ((err = sl_send_data(q, 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(q, M2PA_STATUS_IN_SERVICE)))
					return (err);
			}
			if (sl->flags & MF_REM_INS) {
				if ((err = sl_in_service_ind(q)))
					return (err);
				sl->flags |= MF_SEND_MSU;
				sl->flags |= MF_RECV_MSU;
				sl_is_stats(q);
				sl->state = MS_IN_SERVICE;
			} else {
				sl_timer_start(q, t1);
				sl->state = MS_ALIGNED_READY;
			}
		}
		return (0);
	}
	ptrace(("Received timeout t4 in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_status_in_service(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q)))
			return (err);
		sl_timer_stop(q, t1);
		sl->flags &= ~MF_SEND_MSU;
		sl->flags &= ~MF_RECV_MSU;
		sl_oos_stats(q);
		sl->state = MS_PROCESSOR_OUTAGE;
		return (0);
	case MS_ALIGNED_READY:
		if ((err = sl_in_service_ind(q)))
			return (err);
		sl_timer_stop(q, t1);
		sl->flags |= MF_SEND_MSU;
		sl->flags |= MF_RECV_MSU;
		sl_is_stats(q);
		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 INLINE int sl_rb_congestion_function(queue_t * q);
STATIC INLINE int sl_rx_wakeup(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *mp;
	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(q, 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(q);
				if (!(sl->flags & MF_CONG_ACCEPT))
					sl->rack += xchg(&sl->rmsu, 0);
			}
		}
	case MS_POWER_OFF:
	case MS_OUT_OF_SERVICE:
		return (0);
	}
}

/*
 *  Sequence number comparisons:
 */
/* is s2<=s1<=s3 */
#define between32(__s1,__s2,__s3) ((uint32_t)(__s3)-(uint32_t)(__s2)>=(uint32_t)(__s1)-(uint32_t)(__s2))
#define outside32(__s1,__s2,__s3) between32(__s1,__s3,__s2)
#define before32(__s1,__s2) (((int32_t)((uint32_t)(__s1)-(uint32_t)(__s2))<0))
#define after32(__s1,__s2) (((int32_t)((uint32_t)(__s2)-(uint32_t)(__s1))<0))
#define diff32(__s1,__s2) (before32(__s1,__s2) ? \
		((int32_t)((uint32_t)(__s2)-(uint32_t)(__s1))) : \
		((int32_t)((uint32_t)(__s1)-(uint32_t)(__s2))))
/* is s2<=s1<=s3 */
#define between16(__s1,__s2,__s3) ((uint16_t)(__s3)-(uint16_t)(__s2)>=(uint16_t)(__s1)-(uint16_t)(__s2))
#define outside16(__s1,__s2,__s3) between16(__s1,__s3,__s2)
#define before16(__s1,__s2) (((int16_t)((uint16_t)(__s1)-(uint16_t)(__s2))<0))
#define after16(__s1,__s2) (((int16_t)((uint16_t)(__s2)-(uint16_t)(__s1))<0))
#define diff16(__s1,__s2) (before16(__s1,__s2) ? \
		((int16_t)((uint16_t)(__s2)-(uint16_t)(__s1))) : \
		((int16_t)((uint16_t)(__s1)-(uint16_t)(__s2))))

STATIC INLINE int sl_txc_datack(queue_t * q, ulong count);
STATIC INLINE int sl_rc_sn_check(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		break;
	case M2PA_VERSION_DRAFT4:
	case M2PA_VERSION_DRAFT4_1:
	{
		size_t mlen = mp->b_wptr > mp->b_rptr ? mp->b_wptr - mp->b_rptr : 0;
		if (mlen >= 2 * sizeof(uint16_t)) {
			int msu;
			uint bsnr = ntohs(*((uint16_t *) mp->b_rptr)++);
			uint fsnr = ntohs(*((uint16_t *) mp->b_rptr)++);
			mlen -= 2 * sizeof(uint16_t);
			msu = (mlen > sizeof(uint32_t)) ? 0 : 1;
			if ((uint16_t) fsnr != (uint16_t) (sl->fsnx - msu)) {
				if (sl->bfsn) {
					if (sl->notify.events & SL_EVT_FAIL_ABNORMAL_FIBR)
						if ((err = lmi_event_ind(q, SL_EVT_FAIL_ABNORMAL_FIBR, 0, NULL, 0)))
							return (err);
					if ((err = sl_lsc_out_of_service(q, SL_FAIL_ABNORMAL_FIBR)))
						return (err);
					ptrace(("Link failed: Abnormal FIBR\n"));
					return (-EPROTO);
				}
				ptrace(("Received bad fsn = %u, expecting %u\n", fsnr, (sl->fsnx - msu)));
				sl->bfsn++;
				return (-EPROTO);
			} else
				sl->bfsn = 0;
			if (!between16(bsnr, sl->bsnr, sl->fsnt)) {
				if (sl->bbsn) {
					if (sl->notify.events & SL_EVT_FAIL_ABNORMAL_BSNR)
						if ((err = lmi_event_ind(q, SL_EVT_FAIL_ABNORMAL_BSNR, 0, NULL, 0)))
							return (err);
					if ((err = sl_lsc_out_of_service(q, SL_FAIL_ABNORMAL_BSNR)))
						return (err);
					ptrace(("Link failed: Abnormal BSNR\n"));
					return (-EPROTO);
				}
				ptrace(("Received bad bsn = %u, expecting %u\n", bsnr, sl->fsnt));
				sl->bbsn++;
			} else {
				if ((err = sl_txc_datack(q, diff16(sl->bsnr, bsnr))))
					return (err);
				sl->bsnr = bsnr;
				sl->bbsn = 0;
			}
			break;
		}
		rare();
		return (-EMSGSIZE);
	}
	default:
	case M2PA_VERSION_DRAFT4_9:
	{
		size_t mlen = mp->b_wptr > mp->b_rptr ? mp->b_wptr - mp->b_rptr : 0;
		if (mlen >= 2 * sizeof(uint32_t)) {
			int msu;
			uint bsnr = ntohl(*((uint32_t *) mp->b_rptr)++);
			uint fsnr = ntohl(*((uint32_t *) mp->b_rptr)++);
			mlen -= 2 * sizeof(uint32_t);
			msu = (mlen > sizeof(uint32_t)) ? 0 : 1;
			if ((uint32_t) fsnr != (uint32_t) (sl->fsnx - msu)) {
				if (sl->bfsn) {
					if (sl->notify.events & SL_EVT_FAIL_ABNORMAL_FIBR)
						if ((err = lmi_event_ind(q, SL_EVT_FAIL_ABNORMAL_FIBR, 0, NULL, 0)))
							return (err);
					if ((err = sl_lsc_out_of_service(q, SL_FAIL_ABNORMAL_FIBR)))
						return (err);
					ptrace(("Link failed: Abnormal FIBR\n"));
					return (-EPROTO);
				}
				ptrace(("Received bad fsn = %u, expecting %u\n", fsnr, (sl->fsnx - msu)));
				sl->bfsn++;
				return (-EPROTO);
			} else
				sl->bfsn = 0;
			if (!between32(bsnr, sl->bsnr, sl->fsnt)) {
				if (sl->bbsn) {
					if (sl->notify.events & SL_EVT_FAIL_ABNORMAL_BSNR)
						if ((err = lmi_event_ind(q, SL_EVT_FAIL_ABNORMAL_BSNR, 0, NULL, 0)))
							return (err);
					if ((err = sl_lsc_out_of_service(q, SL_FAIL_ABNORMAL_BSNR)))
						return (err);
					ptrace(("Link failed: Abnormal BSNR\n"));
					return (-EPROTO);
				}
				ptrace(("Received bad bsn = %u, expecting %u\n", bsnr, sl->fsnt));
				sl->bbsn++;
			} else {
				if ((err = sl_txc_datack(q, diff32(sl->bsnr, bsnr))))
					return (err);
				sl->bsnr = bsnr;
				sl->bbsn = 0;
			}
			break;
		}
		rare();
		return (-EMSGSIZE);
	}
	}
	return (0);
}

STATIC INLINE int sl_rc_signal_unit(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	switch (sl->state) {
	case MS_ALIGNED_READY:
		if ((err = sl_in_service_ind(q)))
			return (err);
		sl_timer_stop(q, t1);
		sl->flags |= MF_SEND_MSU;
		sl->flags |= MF_RECV_MSU;
		sl_is_stats(q);
		sl->state = MS_IN_SERVICE;
	default:
		if ((err = sl_rc_sn_check(q, mp)))
			return (err);
		if (sl->flags & MF_RECV_MSU) {
			if (!(sl->flags & MF_CONG_DISCARD)) {
				if (mp->b_wptr - mp->b_rptr > sizeof(uint32_t)) {
					bufq_queue(&sl->rb, mp);
					sl_rb_congestion_function(q);
					sl->fsnx++;
				}
				return (0);
			}
			return (-EBUSY);
		}
		rare();
		return (-EPROTO);
	case MS_POWER_OFF:
	case MS_OUT_OF_SERVICE:
		ptrace(("Received SIGNAL UNIT in unexpected state %u\n", sl->state));
		return (0);
	}
}

STATIC INLINE int sl_tx_wakeup(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *mp;
	switch (sl->state) {
	default:
		if (sl->rack) {
			if ((err = sl_send_ack(q, 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(q, t6);
				else
					sl_timer_start(q, t7);
			}
			while ((mp = sl->tb.q_head)) {
				if ((err = sl_send_data(q, 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(queue_t * q);
STATIC INLINE int sl_txc_datack(queue_t * q, ulong count)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (!count)
		ptrace(("WARNING: ack called with zero count\n"));
	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 (sl->notify.events & SL_EVT_FAIL_ABNORMAL_BSNR)
						if ((err = lmi_event_ind(q, SL_EVT_FAIL_ABNORMAL_BSNR, 0, NULL, 0)))
							return (err);
					if ((err = sl_lsc_out_of_service(q, SL_FAIL_ABNORMAL_BSNR)))
						return (err);
					ptrace(("Link failed: Abnormal BSNR\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(q, t7);
			sl_timer_stop(q, t6);
		} else if (!(sl->flags & MF_REM_BUSY))
			sl_timer_start(q, t7);
		sl->flags &= ~MF_RTB_FULL;
		sl_check_congestion(q);
		return (0);
		/* let tx_wakeup clear up new transmissions later */
	default:
		ptrace(("Received ACK in unexpected state %u\n", sl->state));
		return (0);
	}
}
STATIC INLINE int sl_lsc_pdu(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	bufq_queue(&sl->tb, mp);
	if ((err = sl_check_congestion(q)))
		return (err);
	return (0);
	/* let tx_wakeup clear up the tb later */
}
STATIC INLINE int sl_t7_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->notify.events & SL_EVT_FAIL_ACK_TIMEOUT)
		if ((err = lmi_event_ind(q, SL_EVT_FAIL_ACK_TIMEOUT, 0, NULL, 0)))
			return (err);
	if ((err = sl_lsc_out_of_service(q, SL_FAIL_ACK_TIMEOUT)))
		return (err);
	ptrace(("Link failed: T7 Timeout\n"));
	return (0);
}
STATIC INLINE int sl_lsc_status_busy(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, t6);
			sl_timer_stop(q, 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 INLINE int sl_lsc_congestion_discard(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (!(sl->flags & MF_LOC_BUSY)) {
		if ((err = sl_send_status(q, 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 INLINE int sl_lsc_congestion_accept(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (!(sl->flags & MF_LOC_BUSY)) {
		if ((err = sl_send_status(q, 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 INLINE int sl_lsc_status_busy_ended(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, t6);
		if (sl->rtb.q_count)
			sl_timer_start(q, t7);
		return (0);
	case MS_POWER_OFF:
	case MS_OUT_OF_SERVICE:
		return (0);
	}
}
STATIC INLINE int sl_t6_expiry(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->notify.events & SL_EVT_FAIL_CONG_TIMEOUT)
		if ((err = lmi_event_ind(q, SL_EVT_FAIL_CONG_TIMEOUT, 0, NULL, 0)))
			return (err);
	if ((err = sl_lsc_out_of_service(q, SL_FAIL_CONG_TIMEOUT)))
		return (err);
	ptrace(("Link failed: T6 Timeout\n"));
	return (0);
}
STATIC INLINE int sl_lsc_no_congestion(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (sl->flags & MF_LOC_BUSY)
		if ((err = sl_send_status(q, 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 INLINE int sl_lsc_status_processor_outage(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	switch (sl->state) {
	case MS_OUT_OF_SERVICE:
	case MS_NOT_ALIGNED:
	case MS_ALIGNED:
	case MS_PROVING:
		if (sl->notify.events & SL_EVT_RPO_BEGIN)
			if ((err = lmi_event_ind(q, SL_EVT_RPO_BEGIN, 0, NULL, 0)))
				return (err);
		/* just remember for later */
		sl->flags |= MF_RPO;
		ptrace(("RPO Begins\n"));
		return (0);
	case MS_ALIGNED_READY:
	case MS_ALIGNED_NOT_READY:
		if (sl->notify.events & SL_EVT_RPO_BEGIN)
			if ((err = lmi_event_ind(q, SL_EVT_RPO_BEGIN, 0, NULL, 0)))
				return (err);
		if (!(sl->flags & MF_RPO)) {
			if ((err = sl_remote_processor_outage_ind(q)))
				return (err);
			sl->flags |= MF_RPO;
		}
		sl_rpo_stats(q);
		ptrace(("RPO Begins\n"));
		sl_timer_stop(q, t1);
		sl->state = MS_PROCESSOR_OUTAGE;
		return (0);
	case MS_IN_SERVICE:
		if (sl->notify.events & SL_EVT_RPO_BEGIN)
			if ((err = lmi_event_ind(q, SL_EVT_RPO_BEGIN, 0, NULL, 0)))
				return (err);
		if (!(sl->flags & MF_RPO)) {
			if ((err = sl_remote_processor_outage_ind(q)))
				return (err);
			sl->flags |= MF_RPO;
		}
		ptrace(("RPO Begins\n"));
		sl_timer_stop(q, t7);
		sl_timer_stop(q, 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(q, M2PA_STATUS_BUSY_ENDED)))
					return (err);
			sl->flags &= ~MF_LOC_BUSY;
			break;
		}
		sl_rpo_stats(q);
		sl->state = MS_PROCESSOR_OUTAGE;
		return (0);
	case MS_PROCESSOR_OUTAGE:
		if (sl->notify.events & SL_EVT_RPO_BEGIN)
			if ((err = lmi_event_ind(q, SL_EVT_RPO_BEGIN, 0, NULL, 0)))
				return (err);
		if (!(sl->flags & MF_RPO)) {
			if ((err = sl_remote_processor_outage_ind(q)))
				return (err);
			sl->flags |= MF_RPO;
		}
		ptrace(("RPO Begins\n"));
		return (0);
	}
	ptrace(("Received status PROCESSOR_OUTAGE in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_local_processor_outage(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, M2PA_STATUS_PROCESSOR_OUTAGE)))
			return (err);
		sl->flags |= MF_LPO;
		sl_timer_stop(q, t7);
		sl_timer_stop(q, t6);
		sl->flags &= ~MF_RECV_MSU;
		sl->state = MS_ALIGNED_NOT_READY;
		return (0);
	case MS_IN_SERVICE:
		if ((err = sl_send_status(q, M2PA_STATUS_PROCESSOR_OUTAGE)))
			return (err);
		sl->flags |= MF_LPO;
		sl_timer_stop(q, t7);
		sl_timer_stop(q, 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(q, M2PA_STATUS_BUSY_ENDED)))
					return (err);
			sl->flags &= ~MF_LOC_BUSY;
			break;
		}
		sl_rpo_stats(q);
		sl->state = MS_PROCESSOR_OUTAGE;
		return (0);
	case MS_PROCESSOR_OUTAGE:
		if (!(sl->flags & MF_LPO))
			if ((err = sl_send_status(q, M2PA_STATUS_PROCESSOR_OUTAGE)))
				return (err);
		sl->flags |= MF_LPO;
		sl_timer_stop(q, t7);	/* ??? */
		sl_timer_stop(q, t6);	/* ??? */
		return (0);
	}
	ptrace(("Received primitive SL_LOCAL_PROCESSOR_OUTAGE_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_clear_rtb(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q)))
			return (err);
		if ((err = sl_check_congestion(q)))
			return (err);
		sl_timer_stop(q, t7);
		sl_timer_stop(q, t6);
		sl->flags &= ~MF_LPO;
		if (sl->flags & MF_RPO) {
			if (sl->notify.events & SL_EVT_RPO_END)
				if ((err = lmi_event_ind(q, SL_EVT_RPO_END, 0, NULL, 0)))
					return (err);
			ptrace(("RPO Ends\n"));
			sl_rpr_stats(q);
			sl->flags &= ~MF_RPO;
		}
		sl->flags &= ~MF_SEND_MSU;
		sl->flags |= MF_SEND_MSU;
		if ((err = sl_remote_processor_recovered_ind(q)))
			return (err);
		sl_is_stats(q);
		sl->state = MS_IN_SERVICE;
		return (0);
	}
	ptrace(("Received primitive SL_CLEAR_RTB_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_clear_buffers(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q)))
				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(q)))
				return (err);
			sl->flags &= ~MF_LPO;
			sl_timer_stop(q, t7);	/* ??? */
			sl_timer_stop(q, 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(q, t7);
		sl_timer_stop(q, t6);
		sl->flags &= ~MF_RTB_FULL;
		sl->flags &= ~MF_CLEAR_RTB;
		if ((err = sl_check_congestion(q)))
			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 (sl->notify.events & SL_EVT_RPO_END)
				if ((err = lmi_event_ind(q, SL_EVT_RPO_END, 0, NULL, 0)))
					return (err);
			if ((err = sl_rtb_cleared_ind(q)))
				return (err);
			bufq_purge(&sl->rb);
			flushq(sl->rq, FLUSHDATA);
			if ((err = sl_rb_cleared_ind(q)))
				return (err);
			if (sl->flags & MF_RPO) {
				if ((err = sl_remote_processor_recovered_ind(q)))
					return (err);
				sl->flags &= ~MF_RPO;
			}
			ptrace(("RPO Ends\n"));
			sl_rpr_stats(q);
			break;
		}
		sl->flags |= MF_RECV_MSU;
		sl->flags |= MF_SEND_MSU;
		sl->flags &= ~MF_LPO;
		sl_is_stats(q);
		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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, t6);
				else
					sl_timer_start(q, t7);
			}
			sl_is_stats(q);
			sl->state = MS_IN_SERVICE;
		}
	}
}

#if 0
/* This was the blue book way of doing things... */
STATIC INLINE int sl_lsc_continue(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	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(q, t6);
				else
					sl_timer_start(q, t7);
			}
			sl->flags &= ~MF_LPO;
			sl_is_stats(q);
			sl->state = MS_IN_SERVICE;
		}
		return (0);
	}
	ptrace(("Received primitive SL_CONTINUE_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
#endif
STATIC INLINE int sl_lsc_status_processor_outage_ended(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	switch (sl->state) {
	case MS_PROCESSOR_OUTAGE:
		if (sl->flags & MF_RPO) {
			if (sl->notify.events & SL_EVT_RPO_END)
				if ((err = lmi_event_ind(q, SL_EVT_RPO_END, 0, NULL, 0)))
					return (err);
			if ((err = sl_remote_processor_recovered_ind(q)))
				return (err);
			sl->flags &= ~MF_RPO;
			ptrace(("RPO Ends\n"));
		}
		sl_rpr_stats(q);
		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(q);
			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 INLINE int sl_lsc_resume(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q);
			sl->flags |= MF_RECV_MSU;	/* ??? */
			break;
		case SS7_PVAR_ANSI_92:
			break;
		default:
			if (!(sl->flags & MF_RPO))
				sl_lsc_no_processor_outage(q);
			break;
		}
		sl_timer_stop(q, t7);
		sl_timer_stop(q, 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(q, M2PA_STATUS_PROCESSOR_OUTAGE_ENDED)))
					return (err);
				sl->flags &= ~MF_LPO;
			}
			if (!(sl->flags & MF_RPO))
				sl_lsc_no_processor_outage(q);
			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(q, M2PA_STATUS_BUSY_ENDED)))
					return (err);
			sl->flags &= ~MF_LOC_BUSY;
			sl_timer_stop(q, t7);
			sl_timer_stop(q, 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(q, M2PA_STATUS_PROCESSOR_OUTAGE_ENDED)))
					return (err);
				sl->flags &= ~MF_LPO;
			}
			sl->flags |= MF_RECV_MSU;
			if (sl->flags & MF_RPO) {
				sl_timer_stop(q, t7);
				sl_timer_stop(q, t6);
				sl->flags &= ~MF_SEND_MSU;
				fixme(("We should not do this...\n"));
				if ((err = sl_remote_processor_outage_ind(q)))
					return (err);
				return (0);
			}
			sl->flags |= MF_SEND_MSU;
			if (sl->rtb.q_count) {
				if (sl->flags & MF_REM_BUSY)
					sl_timer_start(q, t6);
				else
					sl_timer_start(q, t7);
			}
			break;
		}
		sl_is_stats(q);
		sl->state = MS_IN_SERVICE;
		return (0);
	}
	ptrace(("Received primitive SL_RESUME_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_status_out_of_service(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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 (sl->notify.events & SL_EVT_FAIL_RECEIVED_SIOS)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_RECEIVED_SIOS, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_RECEIVED_SIOS)))
			return (err);
		ptrace(("Link failed: Received SIOS\n"));
		return (0);
	case MS_ALIGNED:
	case MS_PROVING:
		sl->flags &= ~MF_REM_INS;
		if (sl->notify.events & SL_EVT_FAIL_ALIGNMENT)
			if ((err = lmi_event_ind(q, SL_EVT_FAIL_ALIGNMENT, 0, NULL, 0)))
				return (err);
		if ((err = sl_lsc_out_of_service(q, SL_FAIL_ALIGNMENT_NOT_POSSIBLE)))
			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 INLINE int sl_lsc_retrieve_bsnt(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	switch (sl->state) {
	case MS_OUT_OF_SERVICE:
	case MS_PROCESSOR_OUTAGE:
		sl_bsnt_ind(q, sl->fsnr);
		return (0);
	}
	ptrace(("Received primitive SL_RETRIEVE_BSNT_REQ in unexpected state %u\n", sl->state));
	return (-EPROTO);
}
STATIC INLINE int sl_lsc_retrieval_request_and_fsnc(queue_t * q, ulong fsnc)
{
	sl_t *sl = SL_PRIV(q);
	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(q, 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(q, mp))) {
				bufq_queue_head(&sl->tb, mp);
				return (err);
			}
		}
		if ((err = sl_retrieval_complete_ind(q)))
			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(q)))
		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 INLINE int sl_rb_congestion_function(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, 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(q, 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(q, 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(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	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(q, 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(q, 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(q, 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(q, sl->statem.cong_status, sl->statem.disc_status);
		}
	}
	return (0);
}

/* 
 *  =========================================================================
 *
 *  INPUT Events
 *
 *  =========================================================================
 *  -------------------------------------------------------------------------
 *
 *  SL Peer -> SL Peer (M2PA) Received Messages
 *
 *  -------------------------------------------------------------------------
 */

/* 
 *  M2PA RECV ACK
 *  ---------------------------------------------
 */
STATIC INLINE int sl_recv_datack(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	uint count;
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3_1:
	{
		size_t mlen = mp->b_wptr - mp->b_rptr;
		if (mlen >= sizeof(uint32_t)) {
			if ((count = ntohl(*((uint32_t *) mp->b_wptr)++))) {
				if ((err = sl_txc_datack(q, count)))
					return (err);
				return (0);
			}
			rare();
			return (-EPROTO);
		}
		rare();
		return (-EMSGSIZE);
	}
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT4:
	case M2PA_VERSION_DRAFT4_1:
	default:
	case M2PA_VERSION_DRAFT4_9:
		ptrace(("ERROR: Invalid message for version\n"));
		return (-EPROTO);
	}
}

/*
 *  M2PA RECV PROVING
 *  ---------------------------------------------
 */
STATIC INLINE int sl_recv_proving(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	(void) mp;
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		/* just ignore proving packets */
		return (0);
	case M2PA_VERSION_DRAFT4:
	case M2PA_VERSION_DRAFT4_1:
	default:
	case M2PA_VERSION_DRAFT4_9:
		ptrace(("ERROR: Invalid message for version\n"));
		return (-EPROTO);
	}
}

/* 
 *  M2PA RECV STATUS
 *  ---------------------------------------------
 */
STATIC INLINE int sl_recv_status(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	size_t hlen;		/* M2PA-specific header length */
	size_t mlen = mp->b_wptr > mp->b_rptr ? mp->b_wptr - mp->b_rptr : 0;
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		hlen = 0;
		break;
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
		hlen = 2 * sizeof(uint16_t);
		break;
	case M2PA_VERSION_DRAFT4_9:
		hlen = 2 * sizeof(uint32_t);
		break;
	}
	if (mlen >= sizeof(uint32_t)) {
		uint32_t status = *((uint32_t *) (mp->b_rptr + hlen));
		switch (status) {
		case M2PA_STATUS_BUSY_ENDED:
			return sl_lsc_status_busy_ended(q);
		case M2PA_STATUS_IN_SERVICE:
			return sl_lsc_status_in_service(q);
		case M2PA_STATUS_PROCESSOR_OUTAGE_ENDED:
			return sl_lsc_status_processor_outage_ended(q);
		case M2PA_STATUS_OUT_OF_SERVICE:
			return sl_lsc_status_out_of_service(q);
		case M2PA_STATUS_PROCESSOR_OUTAGE:
			return sl_lsc_status_processor_outage(q);
		case M2PA_STATUS_BUSY:
			return sl_lsc_status_busy(q);
		case M2PA_STATUS_ALIGNMENT:
			return sl_lsc_status_alignment(q);
		case M2PA_STATUS_PROVING_NORMAL:
			return sl_lsc_status_proving_normal(q);
		case M2PA_STATUS_PROVING_EMERGENCY:
			return sl_lsc_status_proving_emergency(q);
		case M2PA_STATUS_ACK:
			return ctrace(sl_rc_signal_unit(q, mp));
		}
		rare();
		return (-EPROTO);
	}
	rare();
	return (-EMSGSIZE);
}

/* 
 *  M2PA RECV DATA
 *  ---------------------------------------------
 */
STATIC INLINE int sl_recv_data(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	size_t hlen;		/* M2PA-specific header length */
	size_t rlen;		/* Routing Label length */
	size_t mlen = mp->b_wptr > mp->b_rptr ? mp->b_wptr - mp->b_rptr : 0;
	switch (sl->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		hlen = 0;
		break;
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
		hlen = 2 * sizeof(uint16_t);
		break;
	case M2PA_VERSION_DRAFT4_9:
		hlen = 2 * sizeof(uint32_t);
		break;
	}
	switch (sl->option.pvar) {
	default:
	case SS7_PVAR_ITUT_88:
	case SS7_PVAR_ITUT_93:
	case SS7_PVAR_ITUT_96:
	case SS7_PVAR_ITUT_00:
	case SS7_PVAR_ETSI_88:
	case SS7_PVAR_ETSI_93:
	case SS7_PVAR_ETSI_96:
	case SS7_PVAR_ETSI_00:
		rlen = 5;
		break;
	case SS7_PVAR_ANSI_88:
	case SS7_PVAR_ANSI_92:
	case SS7_PVAR_ANSI_96:
	case SS7_PVAR_ANSI_00:
	case SS7_PVAR_JTTC_94:
		rlen = 7;
		break;
	}
	if (mlen > hlen + rlen || mlen == 0) {
		if ((err = ctrace(sl_rc_signal_unit(q, mp))))
			return (err);
		return (QR_ABSORBED);	/* absorbed mblk */
	}
	rare();
	return (-EMSGSIZE);
}

/* 
 *  M2PA RECV
 *  ---------------------------------------------
 */
STATIC int sl_recv_msg(queue_t * q, mblk_t * mp)
{
	size_t mlen;
	uint32_t type;
	if (mp->b_wptr >= mp->b_rptr + 2 * sizeof(uint32_t)) {
		int err;
		unsigned char *oldp = mp->b_rptr;
		type = *((uint32_t *) mp->b_rptr)++;
		mlen = ntohl(*((uint32_t *) mp->b_rptr)++) - 2 * sizeof(uint32_t);
		if (mp->b_wptr - mp->b_rptr == PAD4(mlen)) {
			switch (type) {
			case M2PA_DATA_MESSAGE:
				err = sl_recv_data(q, mp);
				break;
			case M2PA_STATUS_MESSAGE:
				err = sl_recv_status(q, mp);
				break;
			case M2PA_PROVING_MESSAGE:
				err = sl_recv_proving(q, mp);
				break;
			case M2PA_ACK_MESSAGE:
				err = sl_recv_datack(q, mp);
				break;
			default:
				rare();
				return (-EPROTO);
			}
			if (err < 0)
				mp->b_rptr = oldp;
			return (err);
		}
		ptrace(("Bad message size %d != %d\n", mp->b_wptr - mp->b_rptr, PAD4(mlen)));
		return (-EMSGSIZE);
	}
	rare();
	return (-EPROTO);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  SL User -> SL Provider (M2PA) Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  LMI_INFO_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_info_req(queue_t * q, mblk_t * mp)
{
	(void) mp;
	return lmi_info_ack(q);
}

/* 
 *  LMI_ATTACH_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_attach_req(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_attach_req_t *p = (lmi_attach_req_t *) mp->b_rptr;
	if (mlen >= sizeof(*p)) {
		if (sl->i_state == LMI_UNATTACHED) {
			sl->i_state = LMI_ATTACH_PENDING;
			ptrace(("PROTO: Primitive is not supported\n"));
			err = LMI_NOTSUPP;
		} else {
			ptrace(("PROTO: would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_ATTACH_REQ, err);
}

/* 
 *  LMI_DETACH_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_detach_req(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_detach_req_t *p = (lmi_detach_req_t *) mp->b_rptr;
	if (mlen >= sizeof(*p)) {
		if (sl->i_state == LMI_DISABLED) {
			sl->i_state = LMI_DETACH_PENDING;
			ptrace(("PROTO: Primitive is not supported\n"));
			err = LMI_NOTSUPP;
		} else {
			ptrace(("PROTO: would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_DETACH_REQ, err);
}

/* 
 *  LMI_ENABLE_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_enable_req(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_enable_req_t *p = (lmi_enable_req_t *) mp->b_rptr;
	if (mlen >= sizeof(*p)) {
		if (sl->i_state == LMI_DISABLED) {
			sl->i_state = LMI_ENABLE_PENDING;
			return lmi_enable_con(q);
		} else {
			ptrace(("PROTO: would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_ENABLE_REQ, err);
}

/* 
 *  LMI_DISABLE_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_disable_req(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_disable_req_t *p = (lmi_disable_req_t *) mp->b_rptr;
	if (mlen >= sizeof(*p)) {
		if (sl->i_state == LMI_ENABLED || sl->i_state == LMI_ENABLE_PENDING) {
			if ((err = sl_lsc_stop(q)))
				return (err);
			sl->i_state = LMI_DISABLE_PENDING;
			return lmi_disable_con(q);
		} else {
			ptrace(("PROTO: would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_DISABLE_REQ, err);
}

/* 
 *  LMI_OPTMGMT_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int lmi_optmgmt_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		ptrace(("PROTO: Primitive is not supported\n"));
		err = LMI_NOTSUPP;
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_OPTMGMT_REQ, err);
}

/* 
 *  SL_PDU_REQ
 *  ---------------------------------------------
 */
#ifndef abs
#define abs(x) ((x)<0 ? -(x):(x))
#endif
STATIC int m_error_reply(queue_t * q, int err)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	switch (err) {
	case QR_DONE:
	case QR_ABSORBED:
	case QR_TRIMMED:
	case QR_LOOP:
	case QR_PASSALONG:
	case QR_PASSFLOW:
	case QR_DISABLE:
		ptrace(("WARNING: Shouldn't pass Q returns to m_error_reply\n"));
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		return (err);
	}
	if ((mp = sl_allocb(q, 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 INLINE int sl_pdu_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		if ((dp = mp->b_cont)) {
			if ((err = sl_lsc_pdu(q, dp)))
				return (err);
			return (QR_TRIMMED);	/* data absorbed */
		} else {
			ptrace(("PROTO: No M_DATA block\n"));
			err = LMI_BADPRIM;
		}
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_EMERGENCY_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_emergency_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_emergency(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_EMERGENCY_CEASES_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_emergency_ceases_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_emergency_ceases(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_START_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_start_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_start(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_STOP_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_stop_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_stop(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_RETRIEVE_BSNT_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_retrieve_bsnt_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_retrieve_bsnt(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_RETRIEVAL_REQUEST_AND_FSNC_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_retrieval_request_and_fsnc_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_retrieval_request_and_fsnc(q, p->sl_fsnc);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_RESUME_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_resume_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_resume(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_CLEAR_BUFFERS_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_clear_buffers_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_clear_buffers(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_CLEAR_RTB_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_clear_rtb_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_clear_rtb(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_LOCAL_PROCESSOR_OUTAGE_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_local_processor_outage_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_local_processor_outage(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_CONGESTION_DISCARD_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_congestion_discard_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_congestion_discard(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_CONGESTION_ACCEPT_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_congestion_accept_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_congestion_accept(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_NO_CONGESTION_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_no_congestion_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_no_congestion(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  SL_POWER_ON_REQ
 *  ---------------------------------------------
 */
STATIC INLINE int sl_power_on_req(queue_t * q, 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;
	if (mlen >= sizeof(*p)) {
		return sl_lsc_power_on(q);
	} else {
		ptrace(("PROTO: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  NPI Provider -> NPI User (M2PA) Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  N_DATA_IND
 *  ---------------------------------------------
 */
STATIC int n_data_ind_slow(queue_t * q, mblk_t * mp)
{
	/*
	 *  Normally we receive data unfragmented and in a singled M_DATA
	 *  block.  This slower routine handles the circumstance where we
	 *  receive fragmented data or data that is chained together in
	 *  multiple M_DATA blocks.
	 */
	sl_t *sl = SL_PRIV(q);
	int err = 0;
	mblk_t *newp = NULL, *dp = mp->b_cont;
	N_data_ind_t *p = ((typeof(p)) mp->b_rptr);
	seldom();
	if (dp->b_cont) {
		/*
		 *  We have chained M_DATA blocks: pull them up.
		 */
		if (!pullupmsg(dp, -1)) {
			/* normall only fail because of dup'ed blocks */
			if (!(newp = msgpullup(dp, -1))) {
				/* normaly only fail because of no buffer */
				if (!sl->rbid)
					sl->rbid = bufcall(xmsgsize(dp), BPRI_MED, &sl_bufsrv, (long) q);
				return (-ENOBUFS);
			}
			dp = newp;
		}
	}
	if (p->DATA_xfer_flags & N_MORE_DATA_FLAG) {
		/*
		 *  We have a partial delivery.  Chain normal message
		 *  together.  We might have a problem with messages split
		 *  over multiple streams?  Treat normal and expedited
		 *  separately.
		 */
		if (sl->rbuf)
			linkb(sl->rbuf, dp);
		else
			sl->rbuf = dp;
	} else {
		if (sl->rbuf) {
			linkb(sl->rbuf, dp);
			dp = xchg(&sl->rbuf, NULL);
		}
		if ((err = sl_recv_msg(q, dp)))
			return (err);
	}
	return (newp ? QR_DONE : QR_TRIMMED);
}
STATIC INLINE int n_data_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *dp;
	N_data_ind_t *p = ((typeof(p)) mp->b_rptr);
	size_t mlen = mp->b_wptr > mp->b_rptr ? mp->b_wptr - mp->b_rptr : 0;
	if (mlen >= sizeof(*p)) {
		if ((dp = mp->b_cont)) {
			if (sl->i_state == LMI_ENABLED) {
				if (!(p->DATA_xfer_flags & N_MORE_DATA_FLAG) && !dp->b_cont) {
					if ((err = sl_recv_msg(q, dp)))
						return (err);
					return (QR_TRIMMED);	/* absorbed data */
				} else
					return n_data_ind_slow(q, mp);
			} else {
				ptrace(("ERROR: Ignore data in other states\n"));
				return (-EPROTO);
			}
		} else {
			ptrace(("ERROR: No data\n"));
			return (-EFAULT);
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		return (-EFAULT);
	}
}

/* 
 *  N_EXDATA_IND
 *  ---------------------------------------------
 */
STATIC int n_exdata_ind_slow(queue_t * q, mblk_t * mp)
{
	/*
	 *  Normally we receive data unfragmented and in a singled M_DATA
	 *  block.  This slower routine handles the circumstance where we
	 *  receive fragmented data or data that is chained together in
	 *  multiple M_DATA blocks.
	 */
	sl_t *sl = SL_PRIV(q);
	int err = 0;
	mblk_t *newp = NULL, *dp = mp->b_cont;
	N_exdata_ind_t *p = ((typeof(p)) mp->b_rptr);
	seldom();
	if (dp->b_cont) {
		/*
		 *  We have chained M_DATA blocks: pull them up.
		 */
		if (!pullupmsg(dp, -1)) {
			/* normall only fail because of dup'ed blocks */
			if (!(newp = msgpullup(dp, -1))) {
				/* normaly only fail because of no buffer */
				if (!sl->rbid)
					sl->rbid = bufcall(xmsgsize(dp), BPRI_MED, &sl_bufsrv, (long) q);
				return (-ENOBUFS);
			}
			dp = newp;
		}
	}
	if ((err = sl_recv_msg(q, dp)))
		return (err);
	return (newp ? QR_DONE : QR_TRIMMED);
}
STATIC INLINE int n_exdata_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mblk_t *dp;
	ensure(mp->b_wptr - mp->b_rptr >= sizeof(N_exdata_ind_t), return (-EFAULT));
	if ((dp = mp->b_cont)) {
		if (sl->i_state == LMI_ENABLED) {
			if (!dp->b_cont) {
				if ((err = sl_recv_msg(q, dp)))
					return (err);
				return (QR_TRIMMED);	/* absorbed data */
			} else
				return n_exdata_ind_slow(q, mp);
		}
		ptrace(("ERROR: Ignore data in other states\n"));
		return (-EPROTO);
	}
	ptrace(("ERROR: No data\n"));
	return (-EFAULT);
}

/* 
 *  N_DATACK_IND
 *  ---------------------------------------------
 */
STATIC INLINE int n_datack_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	ensure(mp->b_wptr - mp->b_rptr >= sizeof(N_datack_ind_t), return (-EFAULT));
	if (sl->version < M2PA_VERSION_DRAFT3_1) {
		if ((err = sl_txc_datack(q, 1)))
			return (err);
		return (0);
	}
	swerr();
	return (-EFAULT);
}

/* 
 *  N_DISCON_IND
 *  ---------------------------------------------
 */
STATIC INLINE int n_discon_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	ensure(mp->b_wptr - mp->b_rptr >= sizeof(N_discon_ind_t), return (-EFAULT));
	if (sl->notify.events & SL_EVT_FAIL_SUERM_EIM)
		if ((err = lmi_event_ind(q, SL_EVT_FAIL_SUERM_EIM, 0, NULL, 0)))
			return (err);
	if ((err = sl_lsc_out_of_service(q, SL_FAIL_SUERM_EIM)))
		return (err);
	ptrace(("Link failed: SUERM/EIM\n"));
	fixme(("Do something here... send a HANGUP or something...\n"));
	return (0);
}

/* 
 *  N_RESET_IND
 *  ---------------------------------------------
 */
STATIC INLINE int n_reset_ind(queue_t * q, mblk_t * mp)
{
	ensure(mp->b_wptr - mp->b_rptr >= sizeof(N_reset_ind_t), return (-EFAULT));
	fixme(("Do something here...\n"));
	return (-EFAULT);
}

/* 
 *  Others...
 *  ---------------------------------------------
 */
STATIC INLINE int n_other_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	(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_PRIV(q);
	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:
		return (QR_PASSALONG);
	}
	if (ret >= 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (QR_ABSORBED);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sl_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	sl_t *sl = SL_PRIV(q);
	ulong oldstate = sl->i_state;
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case LMI_INFO_REQ:
		rtn = lmi_info_req(q, mp);
		break;
	case LMI_ATTACH_REQ:
		rtn = lmi_attach_req(q, mp);
		break;
	case LMI_DETACH_REQ:
		rtn = lmi_detach_req(q, mp);
		break;
	case LMI_ENABLE_REQ:
		rtn = lmi_enable_req(q, mp);
		break;
	case LMI_DISABLE_REQ:
		rtn = lmi_disable_req(q, mp);
		break;
	case LMI_OPTMGMT_REQ:
		rtn = lmi_optmgmt_req(q, mp);
		break;
	case SL_PDU_REQ:
		rtn = sl_pdu_req(q, mp);
		break;
	case SL_EMERGENCY_REQ:
		rtn = sl_emergency_req(q, mp);
		break;
	case SL_EMERGENCY_CEASES_REQ:
		rtn = sl_emergency_ceases_req(q, mp);
		break;
	case SL_START_REQ:
		rtn = sl_start_req(q, mp);
		break;
	case SL_STOP_REQ:
		rtn = sl_stop_req(q, mp);
		break;
	case SL_RETRIEVE_BSNT_REQ:
		rtn = sl_retrieve_bsnt_req(q, mp);
		break;
	case SL_RETRIEVAL_REQUEST_AND_FSNC_REQ:
		rtn = sl_retrieval_request_and_fsnc_req(q, mp);
		break;
	case SL_RESUME_REQ:
		rtn = sl_resume_req(q, mp);
		break;
	case SL_CLEAR_BUFFERS_REQ:
		rtn = sl_clear_buffers_req(q, mp);
		break;
	case SL_CLEAR_RTB_REQ:
		rtn = sl_clear_rtb_req(q, mp);
		break;
	case SL_LOCAL_PROCESSOR_OUTAGE_REQ:
		rtn = sl_local_processor_outage_req(q, mp);
		break;
	case SL_CONGESTION_DISCARD_REQ:
		rtn = sl_congestion_discard_req(q, mp);
		break;
	case SL_CONGESTION_ACCEPT_REQ:
		rtn = sl_congestion_accept_req(q, mp);
		break;
	case SL_NO_CONGESTION_REQ:
		rtn = sl_no_congestion_req(q, mp);
		break;
	case SL_POWER_ON_REQ:
		rtn = sl_power_on_req(q, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
		sl->i_state = oldstate;
	}
	return (rtn);
}
STATIC int sl_r_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case N_DATA_IND:
		rtn = n_data_ind(q, mp);
		break;
	case N_EXDATA_IND:
		rtn = n_exdata_ind(q, mp);
		break;
	case N_DATACK_IND:
		rtn = n_datack_ind(q, mp);
		break;
	case N_DISCON_IND:
		rtn = n_discon_ind(q, mp);
		break;
	case N_RESET_IND:
		rtn = n_reset_ind(q, mp);
		break;
	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(q, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
	}
	return (rtn);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sl_w_data(queue_t * q, mblk_t * mp)
{
	int err;
	if ((err = sl_lsc_pdu(q, mp)))
		return (err);
	return (QR_ABSORBED);	/* data absorbed */
}
STATIC int sl_r_data(queue_t * q, mblk_t * mp)
{
	int err;
	if ((err = sl_recv_msg(q, mp)))
		return (err);
	return (QR_ABSORBED);	/* data absorbed */
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_PCRSE Handling (Events)
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sl_r_pcrse(queue_t * q, mblk_t * mp)
{
	switch (*mp->b_rptr) {
	case t1:
		printd(("T1 Expiry\n"));
		return sl_t1_expiry(q);
	case t2:
		printd(("T2 Expiry\n"));
		return sl_t2_expiry(q);
	case t3:
		printd(("T3 Expiry\n"));
		return sl_t3_expiry(q);
	case t4:
		printd(("T4 Expiry\n"));
		return sl_t4_expiry(q);
#if 0
	case t5:
		printd(("T5 Expiry\n"));
		return sl_t5_expiry(q);
#endif
	case t6:
		printd(("T6 Expiry\n"));
		return sl_t6_expiry(q);
	case t7:
		printd(("T7 Expiry\n"));
		return sl_t7_expiry(q);
#if 0
	case t8:
		printd(("T8 Expiry\n"));
		return sl_t8_expiry(q);
	case t9:
		printd(("T9 Expiry\n"));
		return sl_t9_expiry(q);
#endif
	default:
		swerr();
		return -EFAULT;
	}
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sl_r_error(queue_t * q, mblk_t * mp)
{
	return (QR_PASSALONG);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  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);
	}
	return (QR_PASSALONG);
}
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);
}

/* 
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
STATIC INLINE int sl_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sl_r_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sl_r_proto(q, mp);
	case M_PCRSE:
		return sl_r_pcrse(q, mp);
	case M_ERROR:
		return sl_r_error(q, mp);
	case M_FLUSH:
		return sl_r_flush(q, mp);
	}
	return (QR_PASSFLOW);
}
STATIC INLINE int sl_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sl_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sl_w_proto(q, mp);
	case M_FLUSH:
		return sl_w_flush(q, mp);
	case M_IOCTL:
		return sl_w_ioctl(q, mp);
	}
	return (QR_PASSFLOW);
}

/* 
 *  PUTQ Put Routine
 *  -----------------------------------
 */
STATIC INLINE int sl_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (q->q_count && mp->b_datap->db_type < QPCTL) {
		putq(q, mp);
		return (0);
	}
	if (!sl_trylock(q)) {
		switch ((rtn = proc(q, mp))) {
		case QR_DONE:
			freemsg(mp);
		case QR_ABSORBED:
			break;
		case QR_TRIMMED:
			freeb(mp);
			break;
		case QR_LOOP:
			if (!q->q_next) {
				qreply(q, mp);
				break;
			}
		case QR_PASSALONG:
			if (q->q_next) {
				putnext(q, mp);
				break;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("ERROR: (q dropping) %d\n", rtn));
			freemsg(mp);
			break;
		case QR_DISABLE:
			putq(q, mp);
			rtn = 0;
			break;
		case QR_PASSFLOW:
			if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
				putnext(q, mp);
				break;
			}
		case -ENOBUFS:
		case -EBUSY:
		case -ENOMEM:
		case -EAGAIN:
			putq(q, mp);
			break;
		}
		sl_unlock(q);
	} else {
		rare();
		putq(q, mp);
	}
	return (rtn);
}

/* 
 *  SRVQ Put Routine
 *  -----------------------------------
 */
STATIC INLINE int sl_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;
	ensure(q, return (-EFAULT));
	if (!sl_trylock(q)) {
		mblk_t *mp;
		while ((mp = getq(q))) {
			switch ((rtn = proc(q, mp))) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				continue;
			case QR_TRIMMED:
				freeb(mp);
				continue;
			case QR_LOOP:
				if (!q->q_next) {
					qreply(q, mp);
					continue;
				}
			case QR_PASSALONG:
				if (q->q_next) {
					putnext(q, mp);
					continue;
				}
				rtn = -EOPNOTSUPP;
			default:
				ptrace(("ERROR: (q dropping) %d\n", rtn));
				freemsg(mp);
				continue;
			case QR_DISABLE:
				ptrace(("ERROR: (q disabling) %d\n", rtn));
				noenable(q);
				putbq(q, mp);
				rtn = 0;
				break;
			case QR_PASSFLOW:
				if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
					putnext(q, mp);
					continue;
				}
			case -ENOBUFS:	/* proc must schedule bufcall */
			case -EBUSY:	/* proc must fail canput */
			case -ENOMEM:	/* proc must schedule re-enable */
			case -EAGAIN:	/* proc must schedule re-enable */
				if (mp->b_datap->db_type < QPCTL) {
					ptrace(("ERROR: (q stalled) %d\n", rtn));
					putbq(q, mp);
					break;
				}
				if (mp->b_datap->db_type == M_PCPROTO) {
					mp->b_datap->db_type = M_PROTO;
					mp->b_band = 255;
					putq(q, mp);
					break;
				}
				ptrace(("ERROR: (q dropping) %d\n", rtn));
				freemsg(mp);
				continue;
			}
			break;
		}
		sl_unlock(q);
	} else {
		rare();
		qenable(q);
	}
	return (rtn);
}
STATIC INT sl_rput(queue_t * q, mblk_t * mp)
{
	return (INT) sl_putq(q, mp, &sl_r_prim);
}
STATIC INT sl_rsrv(queue_t * q)
{
	return (INT) sl_srvq(q, &sl_r_prim);
}
STATIC INT sl_wput(queue_t * q, mblk_t * mp)
{
	return (INT) sl_putq(q, mp, &sl_w_prim);
}
STATIC INT sl_wsrv(queue_t * q)
{
	return (INT) sl_srvq(q, &sl_w_prim);
}

/* 
 *  =========================================================================
 *
 *  Private Structure allocation, deallocation and cache
 *
 *  =========================================================================
 */
kmem_cache_t *sl_cachep = NULL;
STATIC void sl_init_caches(void)
{
	if (!sl_cachep)
		if (!(sl_cachep = kmem_find_general_cachep(sizeof(sl_t), GFP_ATOMIC)))
			if (!
			    (sl_cachep =
			     kmem_cache_create("sl_cachep", sizeof(sl_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL)))
				panic("%s:Cannot alloc sl_cachep.\n", __FUNCTION__);
	return;
}
STATIC void sl_term_caches(void)
{
	/* 
	 *  We could try to shrink the caches byt that was a very bad idea
	 *  last time I tried (crashed kernel) so I won't do it here.
	 */
}
STATIC sl_t *sl_alloc_priv(queue_t * q)
{
	sl_t *sl;
	if ((sl = kmem_cache_alloc(sl_cachep, SLAB_ATOMIC))) {
		bzero(sl, sizeof(*sl));
		RD(q)->q_ptr = WR(q)->q_ptr = sl;
		sl->rq = RD(q);
		sl->wq = WR(q);
		sl->version = M2PA_VERSION_DEFAULT;
		sl->i_state = LMI_DISABLED;
		bufq_init(&sl->rb);
		bufq_init(&sl->tb);
		bufq_init(&sl->rtb);
		sl_init_lock(sl);
	}
	return (sl);
}
STATIC void sl_free_priv(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	bufq_purge(&sl->rb);
	bufq_purge(&sl->tb);
	bufq_purge(&sl->rtb);
	if (sl->rbuf)
		freemsg(xchg(&sl->rbuf, NULL));
	untimeout(sl->timer_t1);
	untimeout(sl->timer_t2);
	untimeout(sl->timer_t3);
	untimeout(sl->timer_t4);
#if 0
	untimeout(sl->timer_t5);
#endif
	untimeout(sl->timer_t6);
	untimeout(sl->timer_t7);
#if 0
	untimeout(sl->timer_t8);
	untimeout(sl->timer_t9);
#endif
	kmem_cache_free(sl_cachep, sl);
	return;
}

/* 
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
STATIC int sl_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	(void) crp;		/* for now */
	if (q->q_ptr != NULL)	/* already open */
		return (0);
	if (sflag == MODOPEN || WR(q)->q_next != NULL) {
		//fixme(("Check module we are being pushed over.\n"));
		if (!(sl_alloc_priv(q)))
			return ENOMEM;
		return (0);
	}
	return EIO;
}
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 modnum;
	unless(sl_minfo.mi_idnum, return);
	cmn_err(CE_NOTE, M2PA_BANNER);	/* console splash */
	sl_init_caches();
	if (!(modnum = lis_register_strmod(&sl_info, sl_minfo.mi_idname))) {
		sl_minfo.mi_idnum = 0;
		rare();
		cmn_err(CE_NOTE, "m2pa-sl: couldn't register as module\n");
		return;
	}
	sl_minfo.mi_idnum = modnum;
	return;
}
void sl_terminate(void)
{
	ensure(sl_minfo.mi_idnum, return);
	if ((sl_minfo.mi_idnum = lis_unregister_strmod(&sl_info)))
		cmn_err(CE_WARN, "m2pa-sl: couldn't unregister as module!\n");
	sl_term_caches();
	return;
}

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