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

 @(#) m2pa_sdl.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_sdl.c,v SS7preBetaRelease(0.8.2.7) 2002/04/13 19:27:41"

static char const ident[] = "m2pa_sdl.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 "../lock.h"
#include "../debug.h"
#include "../bufq.h"

#define M2PA_DESCRIP	"M2PA/SCTP SIGNALLING DATA LINK (SDL) 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

// #define M2PA_RX_COMPRESSION
#define M2PA_TX_COMPRESSION

typedef void (*bufcall_fnc_t) (long);

/* 
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
STATIC struct module_info m2pa_minfo = {
	0,			/* Module ID number */
	"m2pa-sdl",		/* 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 m2pa_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int m2pa_close(queue_t *, int, cred_t *);
STATIC INT m2pa_rput(queue_t *, mblk_t *);
STATIC INT m2pa_rsrv(queue_t *);

STATIC struct qinit m2pa_rinit = {
	m2pa_rput,		/* Read put (msg from below) */
	m2pa_rsrv,		/* Read queue service */
	m2pa_open,		/* Each open */
	m2pa_close,		/* Last close */
	NULL,			/* Admin (not used) */
	&m2pa_minfo,		/* Information */
	NULL			/* Statistics */
};

STATIC INT m2pa_wput(queue_t *, mblk_t *);
STATIC INT m2pa_wsrv(queue_t *);

STATIC struct qinit m2pa_winit = {
	m2pa_wput,		/* Write put (msg from above) */
	m2pa_wsrv,		/* Write queue service */
	NULL,			/* Each open */
	NULL,			/* Last close */
	NULL,			/* Admin (not used) */
	&m2pa_minfo,		/* Information */
	NULL			/* Statistics */
};

STATIC struct streamtab m2pa_info = {
	&m2pa_rinit,		/* Upper read queue */
	&m2pa_winit,		/* Upper write queue */
	NULL,			/* Lower read queue */
	NULL			/* Lower write queue */
};

#define QRTN_DONE		0
#define QRTN_ABSORBED	1
#define QRTN_TRIMMED	2
#define QRTN_LOOP		3
#define QRTN_PASSALONG	4
#define QRTN_PASSFLOW	5
#define QRTN_DISABLE	6

/* 
 *  =========================================================================
 *
 *  M2PA Private Structure
 *
 *  =========================================================================
 */
typedef struct m2pa {
	struct m2pa *next;
	queue_t *rq;
	queue_t *wq;
	lis_spin_lock_t lock;	/* queue lock */
	void * users;		/* number of users */
	int rbid;		/* rd bufcall id */
	int wbid;		/* wr bufcall id */
	uint state;		/* SDL interface state */
	uint version;		/* version executing */
	uint flags;		/* interface flags */
	ulong token;		/* my bind token */
	uint rx_status;		/* status of the receiver */
	uint fsnr;		/* FSN for the receiver */
	uint bsnr;		/* BSN for the receiver */
	uint timer_rx_sib;	/* SIB timer for the receiver */
	uint tx_status;		/* status of the transmitter */
	uint fsnt;		/* FSN for the transmitter */
	uint bsnt;		/* BSN for the transmitter */
	uint bsna;		/* BSN acked for the transmitter */
	uint timer_tx_sib;	/* SIB timer for the xmitter */
	size_t count;		/* count of SIE/SINs */
	size_t dacks;		/* data acks outstanding */
	size_t sacks;		/* skipped acks outstanding */
	lmi_option_t options;	/* protocol and variant options */
	sdl_config_t config;	/* SDL configuration options */
	ulong ifclock;		/* device clock mode */
} m2pa_t;

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

#define M2PA_PRIV(__q) ((m2pa_t *)(__q)->q_ptr)
#define m2pa_init_lock(__m2pa) lis_spin_lock_init(&(__m2pa)->lock,"m2pa-sdl-private")
#define m2pa_locked(__m2pa) ((__m2pa)->users)

STATIC INLINE int m2pa_trylock(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	lis_spin_lock_t *lock = &m2->lock;
	lis_spin_lock_bh(lock);
	if (m2->users && m2->users != current) {
		lis_spin_unlock_bh(lock);
		return (!0);
	}
	m2->users = current;
	lis_spin_unlock_bh(lock);
	return (0);
}

STATIC INLINE void m2pa_unlock(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	lis_spin_lock_t *lock = &m2->lock;
	lis_spin_lock_bh(lock);
	if (m2->users && m2->users == current)
		m2->users = NULL;
	else
		ptrace(("Double unlock\n"));
	lis_spin_unlock_bh(lock);
}

#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

#define M2PA_LSSU_SIO	    0
#define M2PA_LSSU_SIN	    1
#define M2PA_LSSU_SIE	    2
#define M2PA_LSSU_SIOS	    3
#define M2PA_LSSU_SIPO	    4
#define M2PA_LSSU_SIB	    5
#define M2PA_FISU	    6
#define M2PA_MSU	    7

#define M2PA_FLAG_RX_ENABLED	0x01
#define M2PA_FLAG_TX_ENABLED	0x02
#define M2PA_FLAG_RX_BUSY	0x04
#define M2PA_FLAG_TX_BUSY	0x08

#define M2PA_RX_SIB_TIME    5
#define M2PA_TX_SIB_TIME    20

#define M2PA_EVENT_TX_SIB_TIMEOUT   1
#define M2PA_EVENT_RX_SIB_TIMEOUT   2

/* 
 *  =========================================================================
 *
 *  Buffer Allocation
 *
 *  =========================================================================
 */
/* 
 *  BUFSRV calls service routine
 *  -------------------------------------------------------------------------
 *  Service a buffer call.
 */
STATIC void m2pa_bufsrv(long data)
{
	queue_t *q = (queue_t *) data;
	if (q) {
		m2pa_t *m2 = M2PA_PRIV(q);
		if (q == m2->rq)
			if (m2->rbid)
				m2->rbid = 0;
		if (q == m2->wq)
			if (m2->wbid)
				m2->wbid = 0;
		qenable(q);
	}
}

/* 
 *  ALLOCB
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *m2pa_allocb(queue_t * q, size_t size, int prior)
{
	mblk_t *mp;
	if ((mp = allocb(size, prior)))
		return (mp);
	{
		m2pa_t *m2 = M2PA_PRIV(q);
		if (q == m2->rq) {
			if (!m2->rbid)
				m2->rbid = bufcall(size, prior, &m2pa_bufsrv, (long) q);
			return (NULL);
		}
		if (q == m2->wq) {
			if (!m2->wbid)
				m2->wbid = bufcall(size, prior, &m2pa_bufsrv, (long) q);
			return (NULL);
		}
	}
	swerr();
	return (NULL);
}

/* 
 *  ESBALLOC
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *m2pa_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);
	{
		m2pa_t *m2 = M2PA_PRIV(q);
		if (q == m2->rq) {
			if (!m2->rbid)
				m2->rbid = esbbcall(prior, &m2pa_bufsrv, (long) q);
			return (NULL);
		}
		if (q == m2->wq) {
			if (!m2->wbid)
				m2->wbid = esbbcall(prior, &m2pa_bufsrv, (long) q);
			return (NULL);
		}
	}
	swerr();
	return (NULL);
}

/* 
 *  =========================================================================
 *
 *  SDL Provider (M2PA) -> SDL User Primitives
 *
 *  =========================================================================
 */
/* 
 *  LMI_INFO_ACK
 *  ---------------------------------------------
 */
STATIC int lmi_info_ack(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_info_ack_t *p;
	if ((mp = m2pa_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 = m2->state;
		p->lmi_max_sdu = -1;
		p->lmi_min_sdu = 0;
		p->lmi_header_len = 0;
		p->lmi_ppa_style = LMI_STYLE1;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_OK_ACK
 *  ---------------------------------------------
 */
#if 0
STATIC int lmi_ok_ack(queue_t * q, long prim)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ((mp = m2pa_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 (m2->state) {
		case LMI_ATTACH_PENDING:
			m2->state = LMI_DISABLED;
			break;
		case LMI_DETACH_PENDING:
			m2->state = LMI_UNATTACHED;
			break;
			/* default is don't change state */
		}
		p->lmi_state = m2->state;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/* 
 *  LMI_ERROR_ACK
 *  ---------------------------------------------
 */
STATIC int lmi_error_ack(queue_t * q, long prim, long err)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_error_ack_t *p;
	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (err);
	}
	if ((mp = m2pa_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 (m2->state) {
		case LMI_ATTACH_PENDING:
			m2->state = LMI_UNATTACHED;
			break;
		case LMI_DETACH_PENDING:
			m2->state = LMI_DISABLED;
			break;
		case LMI_ENABLE_PENDING:
			m2->state = LMI_DISABLED;
			break;
		case LMI_DISABLE_PENDING:
			m2->state = LMI_ENABLED;
			break;
			/* 
			 *  Default is not to change state.
			 */
		}
		p->lmi_state = m2->state;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_ENABLE_CON
 *  ---------------------------------------------
 */
STATIC int lmi_enable_con(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_enable_con_t *p;
	ensure(m2->state == LMI_ENABLE_PENDING, return (-EFAULT));
	if (canputnext(m2->rq)) {
		if ((mp = m2pa_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 = m2->state = LMI_ENABLED;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

/* 
 *  LMI_DISABLE_CON
 *  ---------------------------------------------
 */
STATIC int lmi_disable_con(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_disable_con_t *p;
	ensure(m2->state == LMI_DISABLE_PENDING, return (-EFAULT));
	if (canputnext(m2->rq)) {
		if ((mp = m2pa_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 = m2->state = LMI_DISABLED;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

#if 0
/* 
 *  LMI_OPTMGMT_ACK
 *  ---------------------------------------------
 */
STATIC int lmi_optmgmt_ack(queue_t * q, ulong flags, void *opt_ptr, size_t opt_len)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	if ((mp = m2pa_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(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/* 
 *  LMI_ERROR_IND
 *  ---------------------------------------------
 */
STATIC int lmi_error_ind(queue_t * q, long err)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ((mp = m2pa_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 = m2->state;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
#if 0
STATIC int lmi_stats_ind(queue_t * q, ulong interval, ulong timestamp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_stats_ind_t *p;
	if (bcanputnext(m2->rq, mp->b_band)) {
		if ((mp = m2pa_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(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	ptrace(("INFO: Flow controlled\n"));
	return (-EBUSY);
}
#endif
/* 
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
#if 0
STATIC int lmi_event_ind(queue_t * q, ulong oid, ulong severity, ulong timestamp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	lmi_event_ind_t *p;
	if (bcanputnext(m2->rq, mp->b_band)) {
		if ((mp = m2pa_allocb(q, sizeof(*p), 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 = timestamp;
			p->lmi_severity = severity;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	ptrace(("INFO: Flow controlled\n"));
	return (-EBUSY);
}
#endif
/* 
 *  SDL_DAEDR_RECEIVED_BITS_IND
 *  ---------------------------------------------
 */
STATIC int sdl_read(queue_t * q, mblk_t * dp)
{
	if (canputnext(q)) {
		putnext(q, dp);
		return (0);
	}
	ptrace(("INFO: Flow controlled\n"));
	return (-EBUSY);
}

#if 0
STATIC int sdl_daedr_received_bits_ind(queue_t * q, ulong count, mblk_t * dp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	sdl_daedr_received_bits_ind_t *p;
	if (canputnext(m2->rq)) {
		if ((mp = m2pa_allocb(q, sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((sdl_daedr_received_bits_ind_t *) mp->b_wptr)++;
			p->sdl_primitive = SDL_DAEDR_RECEIVED_BITS_IND;
			p->sdl_count = count;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	ptrace(("INFO: Flow controlled\n"));
	return (-EBUSY);
}
#endif
/* 
 *  SDL_DAEDR_CORRECT_SU_IND
 *  ---------------------------------------------
 */
#if 0
STATIC int sdl_daedr_correct_su_ind(queue_t * q, ulong count)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	sdl_daedr_correct_su_ind_t *p;
	if ((mp = m2pa_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((sdl_daedr_correct_su_ind_t *) mp->b_wptr)++;
		p->sdl_primitive = SDL_DAEDR_CORRECT_SU_IND;
		p->sdl_count = count;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/* 
 *  SDL_DAEDR_SU_IN_ERROR_IND
 *  ---------------------------------------------
 */
#if 0
STATIC int sdl_daedr_su_in_error_ind(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	sdl_daedr_su_in_error_ind_t *p;
	if ((mp = m2pa_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((sdl_daedr_su_in_error_ind_t *) mp->b_wptr)++;
		p->sdl_primitive = SDL_DAEDR_SU_IN_ERROR_IND;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/* 
 *  SDL_DAEDT_TRANSMISSION_REQUEST_IND
 *  ---------------------------------------------
 */
#if 0
STATIC int sdl_daedt_transmission_request_ind(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	sdl_daedt_transmission_request_ind_t *p;
	if ((mp = m2pa_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((sdl_daedt_transmission_request_ind_t *) mp->b_wptr)++;
		p->sdl_primitive = SDL_DAEDT_TRANSMISSION_REQUEST_IND;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/* 
 *  =========================================================================
 *
 *  NPI User (M2PA) -> NPI Provider (SCTP) Primitives
 *
 *  =========================================================================
 */
/* 
 *  N_DATA_REQ
 *  ---------------------------------------------
 */
STATIC int n_data_req(queue_t * q, ulong flags, void *qos_ptr, size_t qos_len, mblk_t * dp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	N_data_req_t *p;
	if (canputnext(m2->wq)) {
		if ((mp = m2pa_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(m2->wq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	ptrace(("INFO: Flow controlled\n"));
	return (-EBUSY);
}

/* 
 *  N_EXDATA_REQ
 *  ---------------------------------------------
 */
STATIC int n_exdata_req(queue_t * q, void *qos_ptr, size_t qos_len, mblk_t * dp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	mblk_t *mp;
	N_exdata_req_t *p;
	if (bcanputnext(m2->wq, 1)) {
		if ((mp = m2pa_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(m2->wq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	ptrace(("INFO: Flow controlled\n"));
	return (-EBUSY);
}

/* 
 *  =========================================================================
 *
 *  M2PA PDU Message Functions
 *
 *  =========================================================================
 */
#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_DATACK	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_DATACK_MESSAGE	\
	__constant_htonl((M2PA_VERSION<<24)|(M2PA_MESSAGE_CLASS<<8)|M2PA_MTYPE_DATACK)
#define M2PA_STATUS_STREAM	0
#define M2PA_DATA_STREAM	1
#if 0
/* 
 *  M2PA SEND PROVING
 *  ---------------------------------------------
 */
STATIC int m2pa_send_proving(queue_t * q, size_t plen)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	if ((mp = m2pa_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)++ = __constant_htonl(2 * sizeof(uint32_t) + plen);
		fixme(("We should include a test pattern.\n"));
		bzero(mp->b_wptr, plen);
		mp->b_wptr += plen;
		if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
			return (0);
		freeb(mp);
		return (err);
	}
	return (-ENOBUFS);
}
#endif

#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_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))

/* 
 *  M2PA SEND STATUS
 *  ---------------------------------------------
 */
STATIC int m2pa_send_status(queue_t * q, uint32_t status)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_STATUS_STREAM, };
#if 0
	{
		const char *r;
		switch (status) {
		case M2PA_STATUS_OUT_OF_SERVICE:
			r = "OUT_OF_SERVICE";
			break;
		case M2PA_STATUS_ALIGNMENT:
			r = "ALIGNMENT";
			break;
		case M2PA_STATUS_PROVING_NORMAL:
			r = "PROVING_NORMAL";
			break;
		case M2PA_STATUS_PROVING_EMERGENCY:
			r = "PROVING_EMERGENCY";
			break;
		case M2PA_STATUS_IN_SERVICE:
			r = "IN_SERVICE";
			break;
		case M2PA_STATUS_PROCESSOR_OUTAGE:
			r = "PROCESSOR_OUTAGE";
			break;
		case M2PA_STATUS_PROCESSOR_ENDED:
			r = "PROCESSOR_ENDED";
			break;
		case M2PA_STATUS_BUSY:
			r = "BUSY";
			break;
		case M2PA_STATUS_BUSY_ENDED:
			r = "BUSY_ENDED";
			break;
		case M2PA_SATUS_ACK:
			r = "ACK";
			break;
		default:
			r = "(unknown)";
			break;
		}
		ptrace(("%x, sending STATUS status = %s\n", (uint) q, r));
	}
#endif
	switch (m2->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		if ((mp = m2pa_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;
			/* XXX: should some of these be sent expedited? */
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			assert(mp->b_datap->db_ref);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
		if ((mp = m2pa_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(m2->bsnt);
			*((uint16_t *) mp->b_wptr)++ = htons(m2->fsnt);
			*((uint32_t *) mp->b_wptr)++ = status;
			/* XXX: should some of these be sent expedited? */
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			assert(mp->b_datap->db_ref);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	case M2PA_VERSION_DRAFT4_9:
		if ((mp = m2pa_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(m2->bsnt);
			*((uint32_t *) mp->b_wptr)++ = htonl(m2->fsnt);
			*((uint32_t *) mp->b_wptr)++ = status;
			/* XXX: should some of these be sent expedited? */
			if (!(err = n_data_req(q, 0, &qos, sizeof(qos), mp)))
				return (0);
			assert(mp->b_datap->db_ref);
			freeb(mp);
			return (err);
		}
		return (-ENOBUFS);
	}
}

STATIC void m2pa_tx_sib_timeout(caddr_t data)
{
	m2pa_t *m2 = (m2pa_t *) data;
	if (!m2->timer_tx_sib)
		return;
	else
		m2->timer_tx_sib = 0;
	if (!(m2->flags & M2PA_FLAG_TX_BUSY))
		return;
	putctl1(m2->rq, M_CTL, M2PA_EVENT_TX_SIB_TIMEOUT);
	return;
}

/* 
 *  M2PA SEND DATA
 *  ---------------------------------------------
 */
STATIC int m2pa_send_datack(queue_t * q, ulong count)
{
	m2pa_t *m2 = M2PA_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 (m2->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 opnly.
		 */
		return (0);
	case M2PA_VERSION_DRAFT3_1:
		// ptrace(("%x, sending DATACK\n",(uint)q));
		if ((mp = m2pa_allocb(q, 3 * sizeof(uint32_t), BPRI_MED))) {
			mp->b_datap->db_type = M_DATA;
			*((uint32_t *) mp->b_wptr)++ = M2PA_DATACK_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);
			assert(mp->b_datap->db_ref);
			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.
		 */
		// return m2pa_send_status(q, M2PA_STATUS_ACK);
		if ((mp = m2pa_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(m2->bsnt);
			*((uint16_t *) mp->b_wptr)++ = htons(m2->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.
		 */
		// return m2pa_send_status(q, M2PA_STATUS_ACK);
		if ((mp = m2pa_allocb(q, 5 * sizeof(uint32_t), BPRI_MED))) {
			*((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(m2->bsnt);
			*((uint32_t *) mp->b_wptr)++ = htonl(m2->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 int m2pa_send_data(queue_t * q, mblk_t * dp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *mp, *db;
	ulong rcpt = (m2->version == M2PA_VERSION_DRAFT3) ? N_RC_FLAG : 0;
	size_t dlen = dp->b_wptr - dp->b_rptr;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	// ptrace(("%x, sending DATA length = %u\n",(uint)q,dlen));
	if ((db = dupmsg(dp))) {
		switch (m2->version) {
		case M2PA_VERSION_DRAFT3:
		case M2PA_VERSION_DRAFT3_1:
			if ((mp = m2pa_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)++ = __constant_htonl(dlen + 2 * sizeof(uint32_t));
				mp->b_cont = db;
				pullupmsg(mp, -1);
				if (!(err = n_data_req(q, rcpt, &qos, sizeof(qos), mp))) {
					if (rcpt)
						m2->dacks++;
					return (0);
				}
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				return (err);
			}
			freemsg(db);
			return (-ENOBUFS);
		case M2PA_VERSION_DRAFT4:
		default:
		case M2PA_VERSION_DRAFT4_1:
			if ((mp = m2pa_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)++ = __constant_htonl(dlen + 3 * sizeof(uint32_t));
				*((uint16_t *) mp->b_wptr)++ = htons(m2->bsnt);
				*((uint16_t *) mp->b_wptr)++ = htons(m2->fsnt);
				mp->b_cont = db;
				pullupmsg(mp, -1);
				if (!(err = n_data_req(q, rcpt, &qos, sizeof(qos), mp)))
					return (0);
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				return (err);
			}
			freemsg(db);
			return (-ENOBUFS);
		case M2PA_VERSION_DRAFT4_9:
			if ((mp = m2pa_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)++ = __constant_htonl(dlen + 4 * sizeof(uint32_t));
				*((uint32_t *) mp->b_wptr)++ = htonl(m2->bsnt);
				*((uint32_t *) mp->b_wptr)++ = htonl(m2->fsnt);
				mp->b_cont = db;
				pullupmsg(mp, -1);
				if (!(err = n_data_req(q, rcpt, &qos, sizeof(qos), mp)))
					return (0);
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				return (err);
			}
			freemsg(db);
			return (-ENOBUFS);
		}
	}
	return (-ENOBUFS);
}

/* 
 *  M2PA SEND
 *  ---------------------------------------------
 */
STATIC int m2pa_send(queue_t * q, mblk_t * dp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	uint status;
	uint acks = 0;
	size_t dlen = dp->b_wptr - dp->b_rptr;
	unsigned char *rptr = dp->b_rptr;
	if (m2->options.popt & SS7_POPT_XSN) {
		if (dlen < 6) {
			rare();
			return (-EMSGSIZE);
		}
		switch (dlen) {
		case 6:
			status = M2PA_FISU;
			m2->bsnt = ntohs(((uint16_t *) dp->b_rptr)[0]) & 0xfff;
			m2->fsnt = ntohs(((uint16_t *) dp->b_rptr)[1]) & 0xfff;
			acks = (m2->bsnt - m2->bsna) & 0xfff;
			break;
		case 7:
			status = dp->b_rptr[6];
			break;
		case 8:
			status = dp->b_rptr[7];
			break;
		default:
			status = M2PA_MSU;
			m2->bsnt = ntohs(((uint16_t *) dp->b_rptr)[0]) & 0xfff;
			m2->fsnt = ntohs(((uint16_t *) dp->b_rptr)[1]) & 0xfff;
			acks = (m2->bsnt - m2->bsna) & 0xfff;
			dp->b_rptr += 5;
			dlen -= 5;
			break;
		}
	} else {
		if (dlen < 3) {
			rare();
			return (-EMSGSIZE);
		}
		switch (dlen) {
		case 3:
			status = M2PA_FISU;
			m2->bsnt = dp->b_rptr[0] & 0x7f;
			m2->fsnt = dp->b_rptr[1] & 0x7f;
			acks = (m2->bsnt - m2->bsna) & 0x7f;
			break;
		case 4:
			status = dp->b_rptr[3];
			break;
		case 5:
			status = dp->b_rptr[4];
			break;
		default:
			status = M2PA_MSU;
			m2->bsnt = dp->b_rptr[0] & 0x7f;
			m2->fsnt = dp->b_rptr[1] & 0x7f;
			acks = (m2->bsnt - m2->bsna) & 0x7f;
			dp->b_rptr += 2;
			dlen -= 2;
			break;
		}
	}
	if (m2->tx_status == M2PA_LSSU_SIPO && (status == M2PA_FISU || status == M2PA_MSU)) {
		if ((err = m2pa_send_status(q, M2PA_STATUS_PROCESSOR_ENDED))) {
			dp->b_rptr = rptr;
			return (err);
		}
	}
	switch (status) {
	case M2PA_FISU:
		if (m2->tx_status != M2PA_FISU && m2->tx_status != M2PA_MSU) {
			if ((err = m2pa_send_status(q, M2PA_STATUS_IN_SERVICE)))
				return (err);
			break;
		}
		if (acks) {
			if ((err = m2pa_send_datack(q, acks)))
				return (err);
			m2->bsna = m2->bsnt;
		}
		break;
	case M2PA_LSSU_SIO:
#ifdef M2PA_TX_COMPRESSION
		if (m2->tx_status == status)
			return (0);
#endif
		if ((err = m2pa_send_status(q, M2PA_STATUS_ALIGNMENT)))
			return (err);
		break;
	case M2PA_LSSU_SIN:
#ifdef M2PA_TX_COMPRESSION
		if (m2->tx_status == status)
			return (0);
#endif
		if ((err = m2pa_send_status(q, M2PA_STATUS_PROVING_NORMAL)))
			return (err);
		/*
		 *  TODO: we need to start sending proving packets.
		 */
		break;
	case M2PA_LSSU_SIE:
#ifdef M2PA_TX_COMPRESSION
		if (m2->tx_status == status)
			return (0);
#endif
		if ((err = m2pa_send_status(q, M2PA_STATUS_PROVING_EMERGENCY)))
			return (err);
		/*
		 *  TODO: we need to start sending proving packets.
		 */
		break;
	case M2PA_LSSU_SIOS:
#ifdef M2PA_TX_COMPRESSION
		if (m2->tx_status == status)
			return (0);
#endif
		if ((err = m2pa_send_status(q, M2PA_STATUS_OUT_OF_SERVICE)))
			return (err);
		break;
	case M2PA_LSSU_SIPO:
#ifdef M2PA_TX_COMPRESSION
		if (m2->tx_status == status)
			return (0);
#endif
		if ((err = m2pa_send_status(q, M2PA_STATUS_PROCESSOR_OUTAGE)))
			return (err);
		break;
	case M2PA_LSSU_SIB:
#ifdef M2PA_TX_COMPRESSION
		if (m2->tx_status == status)
			return (0);
#endif
		untimeout(xchg(&m2->timer_tx_sib, 0));
		m2->timer_tx_sib = timeout(&m2pa_tx_sib_timeout, (caddr_t) m2, M2PA_TX_SIB_TIME);
		if (!(m2->flags & M2PA_FLAG_TX_BUSY)) {
			m2->flags |= M2PA_FLAG_TX_BUSY;
			if ((err = m2pa_send_status(q, M2PA_STATUS_BUSY)))
				return (err);
		}
		break;
	case M2PA_MSU:
		if ((err = m2pa_send_data(q, dp))) {
			dp->b_rptr = rptr;
			return (err);
		}
		if (m2->version == M2PA_VERSION_DRAFT3_1) {
			/*
			 *  Only extension 3.1 needs an acknowledgement that
			 *  is separate from the data.
			 */
			if (acks) {
				if ((err = m2pa_send_datack(q, acks)))
					return (err);
				m2->bsna = m2->bsnt;
			}
		}
		break;
	}
	m2->tx_status = status;
	return (0);
}

/* 
 *  M2PA DELIV FISU
 *  - - - - - - - - - - - - - - - - - - - - - - -
 *  This function delivers a FISU to the SDL User.
 */
STATIC int m2pa_deliver_fisu(queue_t * q, ulong count)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *mp;
	uint bsnr = m2->bsnr;
	uint fsnr = m2->fsnr;
	// ptrace(("%x, delivering FISU\n",(uint)m2));
	if (m2->options.popt & SS7_POPT_XSN) {
		if ((mp = m2pa_allocb(q, 6, BPRI_MED))) {
			bsnr &= 0xfff;
			fsnr &= 0xfff;
			*mp->b_wptr++ = (bsnr >> 0);
			*mp->b_wptr++ = (bsnr >> 8) | 0x80;
			*mp->b_wptr++ = (fsnr >> 0);
			*mp->b_wptr++ = (fsnr >> 8) | 0x80;
			*mp->b_wptr++ = 0;
			*mp->b_wptr++ = 0;
		}
	} else {
		if ((mp = m2pa_allocb(q, 3, BPRI_MED))) {
			bsnr &= 0x7f;
			fsnr &= 0x7f;
			*mp->b_wptr++ = bsnr | 0x80;
			*mp->b_wptr++ = fsnr | 0x80;
			*mp->b_wptr++ = 0;
		}
	}
	if (mp) {
		mp->b_datap->db_type = M_DATA;
		if (!(err = sdl_read(q, mp)))
			return (0);
		// if ( !(err = sdl_daedr_received_bits_ind(q, count, mp)) )
		// return(0);
		assert(mp->b_datap->db_ref);
		freeb(mp);
		return (err);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  M2PA DELIV LSSU
 *  - - - - - - - - - - - - - - - - - - - - - - -
 *  This function delivers an LSSU to the SDL User.
 */
STATIC int m2pa_deliver_lssu(queue_t * q, ulong count, uint8_t status)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *mp;
	uint bsnr = m2->bsnr;
	uint fsnr = m2->fsnr;
#if 0
	{
		const char *r;
		switch (status) {
		case M2PA_LSSU_SIO:
			r = "SIO";
			break;
		case M2PA_LSSU_SIN:
			r = "SIN";
			break;
		case M2PA_LSSU_SIE:
			r = "SIE";
			break;
		case M2PA_LSSU_SIOS:
			r = "SIOS";
			break;
		case M2PA_LSSU_SIPO:
			r = "SIPO";
			break;
		case M2PA_LSSU_SIB:
			r = "SIB";
			break;
		default:
			r = "???";
			break;
		}
		ptrace(("%x, delivering LSSU status = %s\n", (uint) q, r));
	}
#endif
	if (m2->options.popt & SS7_POPT_XSN) {
		ptrace(("*******ERRROR: I'm not testing XSN!\n"));
		if ((mp = m2pa_allocb(q, 7, BPRI_MED))) {
			bsnr &= 0xfff;
			fsnr &= 0xfff;
			*mp->b_wptr++ = (bsnr >> 0);
			*mp->b_wptr++ = (bsnr >> 8) | 0x80;
			*mp->b_wptr++ = (fsnr >> 0);
			*mp->b_wptr++ = (fsnr >> 8) | 0x80;
			*mp->b_wptr++ = 1;
			*mp->b_wptr++ = 0;
			*mp->b_wptr++ = status;
		}
	} else {
		if ((mp = m2pa_allocb(q, 4, BPRI_MED))) {
			bsnr &= 0x7f;
			fsnr &= 0x7f;
			*mp->b_wptr++ = bsnr | 0x80;
			*mp->b_wptr++ = fsnr | 0x80;
			*mp->b_wptr++ = 1;
			*mp->b_wptr++ = status;
		}
	}
	if (mp) {
		mp->b_datap->db_type = M_DATA;
		if (!(err = sdl_read(q, mp)))
			return (0);
		// if ( !(err = sdl_daedr_received_bits_ind(q, count, mp)) )
		// return(0);
		assert(mp->b_datap->db_ref);
		freeb(mp);
		return (err);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  M2PA DELIV MSU
 *  - - - - - - - - - - - - - - - - - - - - - - -
 *  This function delivers an MSU to the SDL User.
 *  The forward sequence number is incremented
 *  upon delivery of each MSU.
 */
STATIC int m2pa_deliver_msu(queue_t * q, mblk_t * dp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *mp, *db;
	uint li0 = *dp->b_rptr++ & 0xa0;
	uint li = dp->b_wptr - dp->b_rptr;
	uint bsnr = m2->bsnr;
	uint fsnr = m2->fsnr;
	// ptrace(("%x, delivering MSU length = %u\n",(uint)q, li));
	// if ( 3 > li || li > m2->config.m+1 )
	// return(-EMSGSIZE);
	if (!(db = dupmsg(dp)))
		return (-ENOBUFS);
	if (m2->options.popt & SS7_POPT_XSN) {
		if ((mp = m2pa_allocb(q, 6, BPRI_MED))) {
			bsnr &= 0xfff;
			fsnr &= 0xfff;
			if (li > 0x1ff)
				li = 0x1ff;
			*mp->b_wptr++ = (bsnr >> 0);
			*mp->b_wptr++ = (bsnr >> 8) | 0x80;
			*mp->b_wptr++ = (fsnr >> 0);
			*mp->b_wptr++ = (fsnr >> 8) | 0x80;
			*mp->b_wptr++ = (li >> 0);
			*mp->b_wptr++ = (li >> 8) | li0;
		}
	} else {
		if ((mp = m2pa_allocb(q, 2, BPRI_MED))) {
			bsnr &= 0x7f;
			fsnr &= 0x7f;
			if (li > 0x3f)
				li = 0x3f;
			*mp->b_wptr++ = bsnr | 0x80;
			*mp->b_wptr++ = fsnr | 0x80;
			*mp->b_wptr++ = li | li0;
		}
	}
	if (mp) {
		mp->b_datap->db_type = M_DATA;
		mp->b_cont = db;
		pullupmsg(mp, -1);
		if (!(err = sdl_read(q, mp)))
			return (0);
		dp->b_rptr--;
		assert(mp->b_datap->db_ref);
		freemsg(mp);
		return (err);
	}
	rare();
	assert(db->b_datap->db_ref);
	freemsg(db);
	return (-ENOBUFS);
}

/* 
 *  M2PA DELIV
 *  ---------------------------------------------
 */
STATIC INLINE int m2pa_deliver_now(queue_t * q, uint8_t status, ulong count, mblk_t * mp)
{
	int err;
	switch (status) {
	case M2PA_FISU:
		if ((err = m2pa_deliver_fisu(q, count)))
			return (err);
		break;
	case M2PA_MSU:
		if ((err = m2pa_deliver_msu(q, mp)))
			return (err);
		break;
	default:
		never();
	case M2PA_LSSU_SIO:
	case M2PA_LSSU_SIN:
	case M2PA_LSSU_SIE:
	case M2PA_LSSU_SIOS:
	case M2PA_LSSU_SIPO:
	case M2PA_LSSU_SIB:
		if ((err = m2pa_deliver_lssu(q, count, status)))
			return (err);
		break;
	}
	return (0);
}
STATIC int m2pa_deliver(queue_t * q, uint8_t status, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
#ifdef M2PA_RX_COMPRESSION
	if (m2->rx_status != M2PA_MSU) {
		if (status == m2->rx_status) {
			m2->count++;
			return (0);
		}
		if (m2->count) {
			if ((err = m2pa_deliver_now(q, m2->rx_status, m2->count, NULL)))
				return (err);
			m2->count = 0;
		}
	}
#endif
	if ((err = m2pa_deliver_now(q, status, 1, mp)))
		return (err);
	m2->rx_status = status;
	return (0);
}
STATIC void m2pa_rx_sib_timeout(caddr_t data)
{
	queue_t *q = (queue_t *) data;
	m2pa_t *m2 = M2PA_PRIV(q);
	if (!m2->timer_rx_sib)
		return;
	else
		m2->timer_rx_sib = 0;
	if (!(m2->flags & M2PA_FLAG_RX_BUSY))
		return;
	m2->timer_rx_sib = timeout(&m2pa_rx_sib_timeout, (caddr_t) q, M2PA_RX_SIB_TIME);
	putctl1(m2->rq, M_CTL, M2PA_EVENT_RX_SIB_TIMEOUT);
	return;
}

/* 
 *  M2PA RECV DATACK
 *  ---------------------------------------------
 */
STATIC INLINE int m2pa_recv_datack(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	uint bsnr = m2->bsnr;
	(void) mp;
	switch (m2->version) {
	case M2PA_VERSION_DRAFT3_1:
		if (mp->b_wptr >= mp->b_rptr + sizeof(uint32_t)) {
			m2->bsnr += ntohl(*((uint32_t *) mp->b_rptr)++);
			if ((err = m2pa_deliver(q, M2PA_FISU, NULL))) {
				m2->bsnr = bsnr;
				return (err);
			}
			return (0);
		}
		rare();
		return (-EMSGSIZE);
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
	case M2PA_VERSION_DRAFT4_9:
		ptrace(("ERROR: Invalid message for version\n"));
		return (-EPROTO);
	}
}

/* 
 *  M2PA RECV PROVING
 *  ---------------------------------------------
 */
STATIC INLINE int m2pa_recv_proving(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	switch (m2->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		if (mp->b_wptr > mp->b_rptr) {
#ifdef M2PA_RX_COMPRESSION
			switch (m2->rx_status) {
			case M2PA_LSSU_SIE:
				m2->count++;
				return (0);
			case M2PA_LSSU_SIN:
				m2->count++;
				return (0);
			}
			if ((err = m2pa_deliver(q, M2PA_LSSU_SIN, NULL)))
				return (err);
#else
			uint status;
			switch (m2->rx_status) {
			default:
			case M2PA_LSSU_SIN:
				status = M2PA_LSSU_SIN;
				break;
			case M2PA_LSSU_SIE:
				status = M2PA_LSSU_SIE;
				break;
			}
			if ((err = m2pa_deliver(q, status, NULL)))
				return (err);
#endif
		}
		return (0);
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
	case M2PA_VERSION_DRAFT4_9:
		ptrace(("ERROR: Invalid message for version\n"));
		return (-EPROTO);
	}
}

/* 
 *  M2PA RECV STATUS
 *  ---------------------------------------------
 */
STATIC INLINE int m2pa_recv_status(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	uint status;
	switch (m2->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
		break;
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
		if (mp->b_wptr >= mp->b_rptr + 2 * sizeof(uint16_t)) {
			m2->bsnr = *((uint16_t *) mp->b_rptr)++;
			m2->fsnr = *((uint16_t *) mp->b_rptr)++;
			break;
		}
		rare();
		return (-EMSGSIZE);
	case M2PA_VERSION_DRAFT4_9:
		if (mp->b_wptr >= mp->b_rptr + 2 * sizeof(uint32_t)) {
			m2->bsnr = *((uint32_t *) mp->b_rptr)++;
			m2->fsnr = *((uint32_t *) mp->b_rptr)++;
			break;
		}
		rare();
		return (-EMSGSIZE);
	}
	if (mp->b_wptr >= mp->b_rptr + sizeof(uint32_t)) {
		switch (*((uint32_t *) mp->b_rptr)++) {
		case M2PA_STATUS_BUSY_ENDED:
			if (m2->flags & M2PA_FLAG_RX_BUSY) {
				m2->flags &= ~M2PA_FLAG_RX_BUSY;
				untimeout(xchg(&m2->timer_rx_sib, 0));
			}
			return (0);
		case M2PA_STATUS_IN_SERVICE:
			status = M2PA_FISU;
			break;
		case M2PA_STATUS_PROCESSOR_ENDED:
			status = M2PA_FISU;
			break;
		case M2PA_STATUS_OUT_OF_SERVICE:
			if (m2->version > M2PA_VERSION_DRAFT3) {
				status = M2PA_LSSU_SIOS;
				break;
			}
			rare();
			return (-EPROTO);
		case M2PA_STATUS_PROCESSOR_OUTAGE:
			status = M2PA_LSSU_SIPO;
			break;
		case M2PA_STATUS_BUSY:
			status = M2PA_LSSU_SIB;
			if (!(m2->flags & M2PA_FLAG_RX_BUSY)) {
				m2->flags |= M2PA_FLAG_RX_BUSY;
				m2->timer_rx_sib = timeout(&m2pa_rx_sib_timeout, (caddr_t) q, M2PA_RX_SIB_TIME);
			}
			break;
		case M2PA_STATUS_ALIGNMENT:
			status = M2PA_LSSU_SIO;
			break;
		case M2PA_STATUS_PROVING_NORMAL:
			status = M2PA_LSSU_SIN;
			break;
		case M2PA_STATUS_PROVING_EMERGENCY:
			status = M2PA_LSSU_SIE;
			break;
		case M2PA_STATUS_ACK:
			if (m2->version > M2PA_VERSION_DRAFT4) {
				status = M2PA_FISU;
				break;
			}
			rare();
			return (-EPROTO);
		default:
			rare();
			return (-EPROTO);
		}
		if ((err = m2pa_deliver(q, status, NULL)))
			return (err);
		return (0);
	}
	rare();
	return (-EMSGSIZE);
}

/* 
 *  M2PA RECV DATA
 *  ---------------------------------------------
 */
STATIC INLINE int m2pa_recv_data(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	switch (m2->version) {
	case M2PA_VERSION_DRAFT3:
	case M2PA_VERSION_DRAFT3_1:
	{
		uint fsnr = m2->fsnr++;
		if ((err = m2pa_deliver(q, M2PA_MSU, mp))) {
			m2->fsnr = fsnr;
			return (err);
		}
		return (0);
	}
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
		if (mp->b_wptr >= mp->b_rptr + 2 * sizeof(uint16_t)) {
			m2->bsnr = *((uint16_t *) mp->b_rptr)++;
			m2->fsnr = *((uint16_t *) mp->b_rptr)++;
			return m2pa_deliver(q, M2PA_MSU, mp);
		}
		rare();
		return (-EMSGSIZE);
	case M2PA_VERSION_DRAFT4_9:
		if (mp->b_wptr >= mp->b_rptr + 2 * sizeof(uint32_t)) {
			m2->bsnr = *((uint32_t *) mp->b_rptr)++;
			m2->fsnr = *((uint32_t *) mp->b_rptr)++;
			return m2pa_deliver(q, M2PA_MSU, mp);
		}
		rare();
		return (-EMSGSIZE);
	}
}

/* 
 *  M2PA RECV
 *  ---------------------------------------------
 */
STATIC int m2pa_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 = m2pa_recv_data(q, mp);
				break;
			case M2PA_STATUS_MESSAGE:
				err = m2pa_recv_status(q, mp);
				break;
			case M2PA_PROVING_MESSAGE:
				err = m2pa_recv_proving(q, mp);
				break;
			case M2PA_DATACK_MESSAGE:
				err = m2pa_recv_datack(q, mp);
				break;
			default:
				rare();
				return (-EPROTO);
			}
			if (err < 0)
				mp->b_rptr = oldp;
			return (err);
		}
		rare();
		return (-EMSGSIZE);
	}
	rare();
	return (-EPROTO);
}

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

/* 
 *  LMI_ATTACH_REQ
 *  ---------------------------------------------
 */
STATIC int lmi_attach_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_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 (m2->state == LMI_UNATTACHED) {
			m2->state = LMI_ATTACH_PENDING;
			ptrace(("PROTO: Primitive not supported\n"));
			err = LMI_NOTSUPP;
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_ATTACH_REQ, err);
}

/* 
 *  LMI_DETACH_REQ
 *  ---------------------------------------------
 */
STATIC int lmi_detach_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_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 (m2->state == LMI_DISABLED) {
			m2->state = LMI_DETACH_PENDING;
			ptrace(("PROTO: Primitive not supported\n"));
			err = LMI_NOTSUPP;
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_DETACH_REQ, err);
}

/* 
 *  LMI_ENABLE_REQ
 *  ---------------------------------------------
 */
STATIC int lmi_enable_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_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 (m2->state == LMI_DISABLED) {
			m2->state = LMI_ENABLE_PENDING;
			return lmi_enable_con(q);
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_ENABLE_REQ, err);
}

/* 
 *  LMI_DISABLE_REQ
 *  ---------------------------------------------
 */
STATIC int lmi_disable_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_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 (m2->state == LMI_ENABLED || m2->state == LMI_ENABLE_PENDING) {
			if (m2->state == LMI_ENABLED) {
				m2->flags &= ~(M2PA_FLAG_RX_BUSY | M2PA_FLAG_RX_BUSY);
				untimeout(xchg(&m2->timer_rx_sib, 0));
				untimeout(xchg(&m2->timer_tx_sib, 0));
				m2pa_send_status(q, M2PA_STATUS_OUT_OF_SERVICE);
			}
			m2->state = LMI_DISABLE_PENDING;
			return lmi_disable_con(q);
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_DISABLE_REQ, err);
}

/* 
 *  LMI_OPTMGMT_REQ
 *  ---------------------------------------------
 */
STATIC 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 not supported\n"));
		err = LMI_NOTSUPP;
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return lmi_error_ack(q, LMI_OPTMGMT_REQ, err);
}

/* 
 *  SDL WRITE
 *  ---------------------------------------------
 *  Just the M_DATA portion of SDL_DAEDT_TRANSMISSION_REQ
 */
#ifndef abs
#define abs(x) ((x)<0 ? -(x):(x))
#endif
STATIC int m_error_reply(queue_t * q, int err)
{
	mblk_t *mp;
	switch (err) {
	case QRTN_DONE:
	case QRTN_ABSORBED:
	case QRTN_TRIMMED:
	case QRTN_LOOP:
	case QRTN_PASSALONG:
	case QRTN_PASSFLOW:
	case QRTN_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 = m2pa_allocb(q, 2, BPRI_HI))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = abs(err);
		*(mp->b_wptr)++ = abs(err);
		qreply(q, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
STATIC int sdl_write(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	if (m2->state != LMI_DISABLED) {
		if (m2->state == LMI_ENABLED) {
			if (m2->flags & M2PA_FLAG_TX_ENABLED) {
				if (!(err = m2pa_send(q, mp)))
					return (0);
			} else {
				ptrace(("INFO: Transmit is DISABLED\n"));
				err = 0;
			}
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("INFO: Interface is DISABLED\n"));
		return (0);
	}
	return m_error_reply(q, err);	/* XXX */
}

/* 
 *  SDL_DAEDT_TRAMSMISSION_REQ
 *  ---------------------------------------------
 */
STATIC int sdl_daedt_xmit_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *dp = mp->b_cont;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sdl_daedt_transmission_req_t *p = (sdl_daedt_transmission_req_t *) mp->b_rptr;
	if (dp) {
		if (mlen >= sizeof(*p)) {
			if (m2->state != LMI_DISABLED) {
				if (m2->state == LMI_ENABLED) {
					if (m2->flags & M2PA_FLAG_TX_ENABLED) {
						if (!(err = m2pa_send(q, dp)))
							return (QRTN_TRIMMED);	/* absorbed
										 * data */
					} else {
						ptrace(("INFO: Transmit is DISABLED\n"));
						err = 0;
					}
				} else {
					ptrace(("PROTO: Would place interface out of state\n"));
					err = LMI_OUTSTATE;
				}
			} else {
				ptrace(("INFO: Interface is DISABLED\n"));
				err = 0;
			}
		} else {
			ptrace(("ERROR: M_PROTO block too short\n"));
			err = LMI_PROTOSHORT;
		}
	} else {
		ptrace(("ERROR: No M_DATA blocks\n"));
		err = -EFAULT;
	}
	return m_error_reply(q, err);	/* XXX */
}

/* 
 *  SDL_DAEDT_START_REQ
 *  ---------------------------------------------
 */
STATIC int sdl_daedt_start_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sdl_daedt_start_req_t *p = (sdl_daedt_start_req_t *) mp->b_rptr;
	if (mlen >= sizeof(*p)) {
		if (m2->state == LMI_ENABLED) {
			/* enable the transmitter section */
			m2->flags |= M2PA_FLAG_TX_ENABLED;
			m2->tx_status = -1;
			return (0);
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);	/* XXX */
}

/* 
 *  SDL_DAEDR_START_REQ
 *  ---------------------------------------------
 */
STATIC int sdl_daedr_start_req(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	sdl_daedr_start_req_t *p = (sdl_daedr_start_req_t *) mp->b_rptr;
	if (mlen >= sizeof(*p)) {
		if (m2->state == LMI_ENABLED) {
			/* enable the receiver section */
			m2->flags |= M2PA_FLAG_RX_ENABLED;
			m2->rx_status = -1;
			m2->count = 0;
			return (0);
		} else {
			ptrace(("PROTO: Would place interface out of state\n"));
			err = LMI_OUTSTATE;
		}
	} else {
		ptrace(("ERROR: M_PROTO block too short\n"));
		err = LMI_PROTOSHORT;
	}
	return m_error_reply(q, err);	/* XXX */
}

/* 
 *  =========================================================================
 *
 *  NPI User (M2PA) <- NPI Provider (SCTP) Primitives
 *
 *  =========================================================================
 */
STATIC int n_other_ind(queue_t * q, mblk_t * mp)
{
	int err;
	(void) mp;
	M2PA_PRIV(q)->state = LMI_DISABLED;
	if ((err = lmi_error_ind(q, LMI_DISC)))
		return (err);
	return (QRTN_DISABLE);
}

/* 
 *  N_CONN_IND
 *  ---------------------------------------------
 */
STATIC int n_conn_ind(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_CONN_CON
 *  ---------------------------------------------
 */
STATIC int n_conn_con(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_DISCON_IND
 *  ---------------------------------------------
 */
STATIC int n_discon_ind(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_DATA_IND
 *  ---------------------------------------------
 */
STATIC int n_data_ind(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	mblk_t *dp;
	ensure(mp->b_wptr - mp->b_rptr >= sizeof(N_data_ind_t), return (-EFAULT));
	if ((dp = mp->b_cont)) {
		if (m2->state == LMI_ENABLED) {
			if (m2->flags & M2PA_FLAG_RX_ENABLED) {
				if ((err = m2pa_recv_msg(q, dp)))
					return (err);
				return (QRTN_TRIMMED);	/* absorbed data */
			}
			return (0);
		}
		rare();
		return (-EPROTO);	/* ignore data in other states */
	}
	rare();
	return (-EFAULT);
}

/* 
 *  N_EXDATA_IND
 *  ---------------------------------------------
 */
STATIC int n_exdata_ind(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_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 (m2->state == LMI_ENABLED) {
			if (m2->flags & M2PA_FLAG_RX_ENABLED) {
				if ((err = m2pa_recv_msg(q, dp)))
					return (err);
				return (QRTN_TRIMMED);	/* absorbed data */
			}
			ptrace(("WARNING: Receive is DISABLED\n"));
			return (0);
		}
		ptrace(("WARNING: Interface is DISABLED\n"));
		return (-EPROTO);
	}
	rare();
	return (-EFAULT);
}

/* 
 *  N_DATACK_IND
 *  ---------------------------------------------
 */
STATIC int n_datack_ind(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	int err;
	ensure(mp->b_wptr - mp->b_rptr >= sizeof(N_datack_ind_t), return (-EFAULT));
	switch (m2->version) {
	case M2PA_VERSION_DRAFT3:
		/*
		 *  draft-ietf-sigtran-m2pa-03.txt does not have any way to
		 *  acknowledge MSUs except by using the underlying
		 *  acknolwedgements of the SCTP transport.
		 */
		if (m2->sacks) {
			m2->sacks--;
			return (0);
		}
		if (m2->dacks) {
			uint bsnr = m2->bsnr;
			m2->bsnr++;
			if ((err = m2pa_deliver(q, M2PA_FISU, NULL))) {
				m2->bsnr = bsnr;
				return (err);
			}
			m2->dacks--;
			return (0);
		}
		seldom();
		return (-EPROTO);
	case M2PA_VERSION_DRAFT3_1:
		/*
		 *  This extended versoin of draft-ietf-sigtran-m2pa-03.txt
		 *  adds an acknolwedgement message type which performs this
		 *  function instead of using the underyling acknowledgements.
		 */
		ptrace(("SWERR: Invalid message for version\n"));
		return (-EPROTO);
	case M2PA_VERSION_DRAFT4:
	default:
	case M2PA_VERSION_DRAFT4_1:
	case M2PA_VERSION_DRAFT4_9:
		/*
		 *  TODO: for draft-ietf-sigtran-m2pa-04.txt and later, we
		 *  might want to use data acknolwedgements during the proving
		 *  period.  We can check that all of the proving messages
		 *  sent were acknowledged before the expiration of the
		 *  proving timer. (I don't do this in the SDL on in the SL.)
		 */
		ptrace(("SWERR: Invalid message for version\n"));
		return (-EPROTO);
	}
}

/* 
 *  N_INFO_ACK
 *  ---------------------------------------------
 */
STATIC int n_info_ack(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_BIND_ACK
 *  ---------------------------------------------
 */
STATIC int n_bind_ack(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_OK_ACK
 *  ---------------------------------------------
 */
STATIC int n_ok_ack(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_ERROR_ACK
 *  ---------------------------------------------
 */
STATIC int n_error_ack(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_RESET_IND
 *  ---------------------------------------------
 */
STATIC int n_reset_ind(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  N_RESET_CON
 *  ---------------------------------------------
 */
STATIC int n_reset_con(queue_t * q, mblk_t * mp)
{
	return n_other_ind(q, mp);
}

/* 
 *  =========================================================================
 *
 *  IO Controls
 *
 *  =========================================================================
 *  I have only implemented enough of these to get the Q.781 test programs
 *  running.
 */
/* 
 *  SDL_IOCGOPTIONS
 *  ---------------------------------------------
 */
STATIC int sdl_iocgoptions(queue_t * q, int cmd, void *arg)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_option_t *opt = (lmi_option_t *) arg;
	*opt = m2->options;
	return (0);
}

/* 
 *  SDL_IOCSOPTIONS
 *  ---------------------------------------------
 */
STATIC int sdl_iocsoptions(queue_t * q, int cmd, void *arg)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_option_t *opt = (lmi_option_t *) arg;
	switch (opt->pvar) {
	case SS7_PVAR_ITUT_88:
	case SS7_PVAR_ITUT_93:
	case SS7_PVAR_ITUT_96:
	case SS7_PVAR_ITUT_00:
	case SS7_PVAR_ANSI_88:
	case SS7_PVAR_ANSI_92:
	case SS7_PVAR_ANSI_96:
	case SS7_PVAR_ANSI_00:
	case SS7_PVAR_ETSI_88:
	case SS7_PVAR_ETSI_93:
	case SS7_PVAR_ETSI_96:
	case SS7_PVAR_ETSI_00:
	case SS7_PVAR_JTTC_94:
		break;
	default:
		rare();
		return (-EINVAL);
	}
	if (opt->popt & ~(SS7_POPT_MPLEV | SS7_POPT_HSL | SS7_POPT_XSN)) {
		rare();
		return (-EINVAL);
	}
	m2->options = *opt;
	return (0);
}

/* 
 *  SDL_IOCGCONFIG
 *  ---------------------------------------------
 */
STATIC int sdl_iocgconfig(queue_t * q, int cmd, void *arg)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	sdl_config_t *cnf = (sdl_config_t *) arg;
	*cnf = m2->config;
	return (0);
}

/* 
 *  SDL_IOCSCONFIG
 *  ---------------------------------------------
 */
STATIC int sdl_iocsconfig(queue_t * q, int cmd, void *arg)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	sdl_config_t *cnf = (sdl_config_t *) arg;
	m2->config = *cnf;
	return (0);
}

/* 
 *  DEV_IOCSIFCLOCK
 *  ---------------------------------------------
 */
STATIC int dev_iocsifclock(queue_t * q, int cmd, void *arg)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	ulong *val = (ulong *) arg;
	switch (*val) {
	case DEV_CLOCK_NONE:
	case DEV_CLOCK_ABR:
	case DEV_CLOCK_SHAPER:
	case DEV_CLOCK_TICK:
		m2->ifclock = *val;
		return (0);
	default:
	case DEV_CLOCK_INT:
	case DEV_CLOCK_EXT:
	case DEV_CLOCK_LOOP:
	case DEV_CLOCK_MASTER:
	case DEV_CLOCK_SLAVE:
	case DEV_CLOCK_DPLL:
		break;
	}
	return (-EINVAL);
}

/* 
 *  DEV_IOCGIFCLOCK
 *  ---------------------------------------------
 */
STATIC int dev_iocgifclock(queue_t * q, int cmd, void *arg)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	ulong *val = (ulong *) arg;
	*val = m2->ifclock;
	return (0);
}

/* 
 *  DEV_IOCCDISCTX
 *  ---------------------------------------------
 *  Disconnect the transmit path.
 */
STATIC int dev_ioccdisctx(queue_t * q, int cmd, void *arg)
{
	return (-EOPNOTSUPP);
}

/* 
 *  DEV_IOCCCONNTX
 *  ---------------------------------------------
 *  Reconnect the transmit path.
 */
STATIC int dev_ioccconntx(queue_t * q, int cmd, void *arg)
{
	return (-EOPNOTSUPP);
}

/* 
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m2pa_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	ulong oldstate = M2PA_PRIV(q)->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 SDL_DAEDT_TRANSMISSION_REQ:
		rtn = sdl_daedt_xmit_req(q, mp);
		break;
	case SDL_DAEDT_START_REQ:
		rtn = sdl_daedt_start_req(q, mp);
		break;
	case SDL_DAEDR_START_REQ:
		rtn = sdl_daedr_start_req(q, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
		seldom();
		M2PA_PRIV(q)->state = oldstate;
	}
	return (rtn);
}
STATIC int m2pa_r_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	switch ((prim = *((ulong *) mp->b_rptr))) {
	case N_CONN_IND:
		rtn = n_conn_ind(q, mp);
		break;
	case N_CONN_RES:
		rtn = n_conn_con(q, mp);
		break;
	case N_DISCON_IND:
		rtn = n_discon_ind(q, mp);
		break;
	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_INFO_ACK:
		rtn = n_info_ack(q, mp);
		break;
	case N_BIND_ACK:
		rtn = n_bind_ack(q, mp);
		break;
	case N_OK_ACK:
		rtn = n_ok_ack(q, mp);
		break;
	case N_ERROR_ACK:
		rtn = n_error_ack(q, mp);
		break;
	case N_RESET_IND:
		rtn = n_reset_ind(q, mp);
		break;
	case N_RESET_CON:
		rtn = n_reset_con(q, mp);
		break;
	default:
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0) {
		seldom();
	}
	return (rtn);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m2pa_w_data(queue_t * q, mblk_t * mp)
{
	int err;
	if ((err = sdl_write(q, mp)))
		return (err);
	return (QRTN_ABSORBED);	/* message absorbed */
}
STATIC int m2pa_r_data(queue_t * q, mblk_t * mp)
{
	int err;
	if ((err = m2pa_recv_msg(q, mp)))
		return (err);
	return (QRTN_ABSORBED);	/* message absorbed */
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling (Events)
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m2pa_r_ctl(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	switch (*mp->b_rptr) {
	case M2PA_EVENT_TX_SIB_TIMEOUT:
		m2->flags &= ~M2PA_FLAG_TX_BUSY;
		m2pa_send_status(q, M2PA_STATUS_BUSY_ENDED);
		return (0);
	case M2PA_EVENT_RX_SIB_TIMEOUT:
		m2pa_deliver(q, M2PA_LSSU_SIB, NULL);
		return (0);
	}
	return (-EOPNOTSUPP);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m2pa_w_ioctl(queue_t * q, mblk_t * mp)
{
	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 = -EINVAL;
	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 SDL_IOC_MAGIC:
		if (count >= size) {
			switch (cmd) {
			case SDL_IOCGOPTIONS:
				ret = sdl_iocgoptions(q, cmd, arg);
				break;
			case SDL_IOCSOPTIONS:
				ret = sdl_iocsoptions(q, cmd, arg);
				break;
			case SDL_IOCGCONFIG:
				ret = sdl_iocgconfig(q, cmd, arg);
				break;
			case SDL_IOCSCONFIG:
				ret = sdl_iocsconfig(q, cmd, arg);
				break;
			default:
			case SDL_IOCTCONFIG:
			case SDL_IOCCCONFIG:
			case SDL_IOCGSTATEM:
			case SDL_IOCCMRESET:
			case SDL_IOCGSTATSP:
			case SDL_IOCSSTATSP:
			case SDL_IOCGSTATS:
			case SDL_IOCCSTATS:
			case SDL_IOCGNOTIFY:
			case SDL_IOCSNOTIFY:
			case SDL_IOCCNOTIFY:
				ret = -EOPNOTSUPP;
				break;
			}
		} else
			ret = -EINVAL;
		break;
	case DEV_IOC_MAGIC:
		if (count >= size) {
			switch (cmd) {
			case DEV_IOCSIFCLOCK:
				ret = dev_iocsifclock(q, cmd, arg);
				break;
			case DEV_IOCGIFCLOCK:
				ret = dev_iocgifclock(q, cmd, arg);
				break;
			case DEV_IOCCDISCTX:
				ret = dev_ioccdisctx(q, cmd, arg);
				break;
			case DEV_IOCCCONNTX:
				ret = dev_ioccconntx(q, cmd, arg);
				break;
			default:
			case DEV_IOCCIFRESET:
			case DEV_IOCGIFFLAGS:
			case DEV_IOCSIFFLAGS:
			case DEV_IOCGIFTYPE:
			case DEV_IOCSIFTYPE:
			case DEV_IOCGGRPTYPE:
			case DEV_IOCSGRPTYPE:
			case DEV_IOCGIFMODE:
			case DEV_IOCSIFMODE:
			case DEV_IOCGIFRATE:
			case DEV_IOCSIFRATE:
			case DEV_IOCGIFCODING:
			case DEV_IOCSIFCODING:
			case DEV_IOCGIFLEADS:
			case DEV_IOCSIFLEADS:
			case DEV_IOCCIFLEADS:
				ret = -EOPNOTSUPP;
				break;
			}
		} else
			ret = -EINVAL;
		break;
	default:
		if (q->q_next) {
			putnext(q, mp);
			return (QRTN_ABSORBED);
		}
		ret = -EOPNOTSUPP;
		break;
	}
	if (ret >= 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		seldom();
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (QRTN_ABSORBED);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m2pa_r_error(queue_t * q, mblk_t * mp)
{
	putnext(q, mp);
	return (QRTN_ABSORBED);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m2pa_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 (QRTN_PASSALONG);
}
STATIC int m2pa_w_flush(queue_t * q, mblk_t * mp)
{
	return m2pa_m_flush(q, mp, FLUSHW);
}
STATIC int m2pa_r_flush(queue_t * q, mblk_t * mp)
{
	return m2pa_m_flush(q, mp, FLUSHR);
}

/* 
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
STATIC int m2pa_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return m2pa_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return m2pa_w_proto(q, mp);
	case M_FLUSH:
		return m2pa_w_flush(q, mp);
	case M_IOCTL:
		return m2pa_w_ioctl(q, mp);
	}
	return (QRTN_PASSFLOW);
}
STATIC int m2pa_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return m2pa_r_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return m2pa_r_proto(q, mp);
	case M_CTL:
		return m2pa_r_ctl(q, mp);
	case M_ERROR:
		return m2pa_r_error(q, mp);
	case M_FLUSH:
		return m2pa_r_flush(q, mp);
	}
	return (QRTN_PASSFLOW);
}

/* 
 *  PUTQ Put Routine
 *  -----------------------------------
 */
STATIC INLINE int m2pa_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) {
		seldom();
		putq(q, mp);
		return (0);
	}
	if (!m2pa_trylock(q)) {
		switch ((rtn = (*proc) (q, mp))) {
		case QRTN_DONE:
			freemsg(mp);
		case QRTN_ABSORBED:
			break;
		case QRTN_TRIMMED:
			freeb(mp);
			break;
		case QRTN_LOOP:
			if (!q->q_next) {
				qreply(q, mp);
				break;
			}
		case QRTN_PASSALONG:
			if (q->q_next) {
				putnext(q, mp);
				break;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("ERROR: (dropping) %d\n", rtn));
			freemsg(mp);
			break;
		case QRTN_DISABLE:
			putq(q, mp);
			rtn = 0;
			break;
		case QRTN_PASSFLOW:
			if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
				putnext(q, mp);
				break;
			}
		case -ENOBUFS:
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			putq(q, mp);
			break;
		}
		m2pa_unlock(q);
	} else {
		rare();
		putq(q, mp);
	}
	return (rtn);
}

/* 
 *  SRVQ Service Routine
 *  -----------------------------------
 */
STATIC INLINE int m2pa_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	int rtn = 0;
	ensure(q, return (-EFAULT));
	if (!m2pa_trylock(q)) {
		mblk_t *mp;
		while ((mp = getq(q))) {
			switch ((rtn = (*proc) (q, mp))) {
			case QRTN_DONE:
				freemsg(mp);
			case QRTN_ABSORBED:
				continue;
			case QRTN_TRIMMED:
				freeb(mp);
				continue;
			case QRTN_LOOP:
				if (!q->q_next) {
					qreply(q, mp);
					continue;
				}
			case QRTN_PASSALONG:
				if (q->q_next) {
					putnext(q, mp);
					continue;
				}
				rtn = -EOPNOTSUPP;
			default:
				ptrace(("ERROR: (q dropping) %d\n", rtn));
				freemsg(mp);
				continue;;
			case QRTN_DISABLE:
				ptrace(("ERROR: (q disabling) %d\n", rtn));
				noenable(q);
				putbq(q, mp);
				rtn = 0;
				break;
			case QRTN_PASSFLOW:
				if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
					putnext(q, mp);
					continue;
				}
			case -ENOBUFS:	/* proc must scheudle bufcall */
			case -EBUSY:	/* proc must fail canput */
			case -EAGAIN:	/* proc must schedule re-enable */
			case -ENOMEM:	/* 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;
		}
		m2pa_unlock(q);
	} else {
		rare();
		qenable(q);
	}
	return (rtn);
}
STATIC INT m2pa_rput(queue_t * q, mblk_t * mp)
{
	return (INT) m2pa_putq(q, mp, &m2pa_r_prim);
}
STATIC INT m2pa_rsrv(queue_t * q)
{
	return (INT) m2pa_srvq(q, &m2pa_r_prim);
}
STATIC INT m2pa_wput(queue_t * q, mblk_t * mp)
{
	return (INT) m2pa_putq(q, mp, &m2pa_w_prim);
}
STATIC INT m2pa_wsrv(queue_t * q)
{
	return (INT) m2pa_srvq(q, &m2pa_w_prim);
}

/* 
 *  =========================================================================
 *
 *  Private Structure allocation, deallocation and cache
 *
 *  =========================================================================
 */
kmem_cache_t *m2pa_cachep = NULL;
STATIC void m2pa_init_caches(void)
{
	if (!m2pa_cachep)
		if (!(m2pa_cachep = kmem_find_general_cachep(sizeof(m2pa_t), GFP_ATOMIC)))
			if (!(m2pa_cachep = kmem_cache_create("m2pa_cachep", sizeof(m2pa_t),
							      0, SLAB_HWCACHE_ALIGN, NULL, NULL)))
				panic("%s:Cannot alloc m2pa_cachep.\n",__FUNCTION__);
	return;
}
STATIC void m2pa_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 m2pa_t *m2pa_alloc_priv(queue_t * q)
{
	m2pa_t *m2;
	if ((m2 = kmem_cache_alloc(m2pa_cachep, SLAB_ATOMIC))) {
		bzero(m2, sizeof(*m2));
		RD(q)->q_ptr = WR(q)->q_ptr = m2;
		m2->rq = RD(q);
		m2->wq = WR(q);
		m2->version = M2PA_VERSION_DEFAULT;
		m2->fsnr = -1;
		m2->bsnr = -1;
		m2->fsnt = -1;
		m2->bsnt = -1;
		m2->bsna = -1;
		m2->state = LMI_DISABLED;
		m2pa_init_lock(m2);
	}
	return (m2);
}
STATIC void m2pa_free_priv(queue_t * q)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	unbufcall(xchg(&m2->rbid, 0));
	unbufcall(xchg(&m2->wbid, 0));
	untimeout(xchg(&m2->timer_rx_sib, 0));
	untimeout(xchg(&m2->timer_tx_sib, 0));
	kmem_cache_free(m2pa_cachep, m2);
	return;
}

/* 
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
STATIC int m2pa_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 (!(m2pa_alloc_priv(q)))
			return ENOMEM;
		return (0);
	}
	return EIO;
}
STATIC int m2pa_close(queue_t * q, int flag, cred_t * crp)
{
	(void) flag;
	(void) crp;
	m2pa_free_priv(q);
	return (0);
}

/* 
 *  =========================================================================
 *
 *  Lis Module Initialization
 *
 *  =========================================================================
 */
void m2pa_init(void)
{
	int modnum;
	unless(m2pa_minfo.mi_idnum, return);
	cmn_err(CE_NOTE, M2PA_BANNER);	/* console splash */
	m2pa_init_caches();
	if (!(modnum = lis_register_strmod(&m2pa_info, m2pa_minfo.mi_idname))) {
		m2pa_minfo.mi_idnum = 0;
		rare();
		cmn_err(CE_NOTE, "m2pa: couldn't register as module\n");
		return;
	}
	m2pa_minfo.mi_idnum = modnum;
	return;
}
void m2pa_terminate(void)
{
	ensure(m2pa_minfo.mi_idnum, return);
	if ((m2pa_minfo.mi_idnum = lis_unregister_strmod(&m2pa_info)))
		cmn_err(CE_WARN, "m2pa: couldn't unregister as module!\n");
	m2pa_term_caches();
	return;
}

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