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

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

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

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

 All Rights Reserved.

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

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

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

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

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

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

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

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

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

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

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

#ifndef INT
#define INT void
#endif

//#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		*/
};

/*
 *  =========================================================================
 *
 *  M2PA Private Structure
 *
 *  =========================================================================
 */
typedef struct m2pa {
	struct m2pa	*next;
	queue_t		*rq;
	queue_t		*wq;
	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;

#define M2PA_VERSION_DRAFT3	3
#define M2PA_VERSION_DRAFT4	4
#define M2PA_VERSION_DEFAULT	M2PA_VERSION_DRAFT4

#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

/*
 *  =========================================================================
 *
 *  SDL Provider (M2PA) -> SDL User Primitives
 *
 *  =========================================================================
 */
/*
 *  LMI_INFO_ACK
 *  ---------------------------------------------
 */
static int
lmi_info_ack(m2pa_t *m2)
{
	mblk_t *mp;
	lmi_info_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_info_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_INFO_ACK;
		p->lmi_version		= 1;
		p->lmi_state		= 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(m2pa_t *m2, long prim)
{
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_ok_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_OK_ACK;
		p->lmi_correct_primitive= prim;
		switch ( 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(m2pa_t *m2, long prim, long err)
{
	mblk_t *mp;
	lmi_error_ack_t *p;
	switch ( err )
	{
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
		case -ENOBUFS:
			seldom(); return(err);
	}
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_ERROR_ACK;
		p->lmi_errno		= err<0 ? -err       : 0;
		p->lmi_reason		= err<0 ? LMI_SYSERR : err;
		p->lmi_error_primitive	= prim;
		switch ( 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(m2pa_t *m2)
{
	mblk_t *mp;
	lmi_enable_con_t *p;
	ensure( m2->state == LMI_ENABLE_PENDING, return(-EFAULT) );
	if ( canputnext(m2->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_enable_con_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_ENABLE_CON;
		p->lmi_state		= m2->state = LMI_ENABLED;
		putnext(m2->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} rare();   return(-EBUSY);
}
/*
 *  LMI_DISABLE_CON
 *  ---------------------------------------------
 */
static int
lmi_disable_con(m2pa_t *m2)
{
	mblk_t *mp;
	lmi_disable_con_t *p;
	ensure( m2->state == LMI_DISABLE_PENDING, return(-EFAULT) );
	if ( canputnext(m2->rq) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_disable_con_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_DISABLE_CON;
		p->lmi_state		= 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(m2pa_t *m2, ulong flags, void *opt_ptr, size_t opt_len)
{
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_optmgmt_ack_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_OPTMGMT_ACK;
		p->lmi_opt_length	= opt_len;
		p->lmi_opt_offset	= opt_len?sizeof(*p):0;
		p->lmi_mgmt_flags	= flags;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		putnext(m2->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
#endif
/*
 *  LMI_ERROR_IND
 *  ---------------------------------------------
 */
static int
lmi_error_ind(m2pa_t *m2, long err)
{
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ind_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_ERROR_IND;
		p->lmi_errno		= err<0 ? -err       : 0;
		p->lmi_reason		= err<0 ? LMI_SYSERR : err;
		p->lmi_state		= m2->state;
		putnext(m2->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
}
/*
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
#if 0
static int
lmi_stats_ind(m2pa_t *m2, ulong interval, ulong timestamp)
{
	mblk_t *mp;
	lmi_stats_ind_t *p;
	if ( bcanputnext(m2->rq, mp->b_band) ) {
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((lmi_stats_ind_t *)mp->b_wptr)++;
		p->lmi_primitive	= LMI_STATS_IND;
		p->lmi_interval		= interval;
		p->lmi_timestamp	= timestamp;
		putnext(m2->rq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
#endif
/*
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
#if 0
static int
lmi_event_ind(m2pa_t *m2, ulong oid, ulong severity, ulong timestamp)
{
	mblk_t *mp;
	lmi_event_ind_t *p;
	if ( bcanputnext(m2->rq, mp->b_band) ) {
	if ( (mp = allocb(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);
	} seldom(); return(-EBUSY);
}
#endif
/*
 *  SDL_DAEDR_RECEIVED_BITS_IND
 *  ---------------------------------------------
 */
static int
sdl_read(m2pa_t *m2, mblk_t *dp)
{
	if ( canputnext(m2->rq) ) {
		putnext(m2->rq, dp);
		return(0);
	} seldom(); return(-EBUSY);
}
#if 0
static int
sdl_daedr_received_bits_ind(m2pa_t *m2, ulong count, mblk_t *dp)
{
	mblk_t *mp;
	sdl_daedr_received_bits_ind_t *p;
	if ( canputnext(m2->rq) ) {
	if ( (mp = allocb(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);
	} seldom(); return(-EBUSY);
}
#endif
/*
 *  SDL_DAEDR_CORRECT_SU_IND
 *  ---------------------------------------------
 */
#if 0
static int
sdl_daedr_correct_su_ind(m2pa_t *m2, ulong count)
{
	mblk_t *mp;
	sdl_daedr_correct_su_ind_t *p;
	if ( (mp = allocb(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(m2pa_t *m2)
{
	mblk_t *mp;
	sdl_daedr_su_in_error_ind_t *p;
	if ( (mp = allocb(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(m2pa_t *m2)
{
	mblk_t *mp;
	sdl_daedt_transmission_request_ind_t *p;
	if ( (mp = allocb(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(m2pa_t *m2, ulong flags, void *qos_ptr, size_t qos_len, mblk_t *dp)
{
	mblk_t *mp;
	N_data_req_t *p;
	if ( canputnext(m2->wq) ) {
	if ( (mp = allocb(sizeof(*p)+qos_len, BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((N_data_req_t *)mp->b_wptr)++;
		p->PRIM_type		= N_DATA_REQ;
		p->DATA_xfer_flags	= flags;
		bcopy(qos_ptr, mp->b_wptr, qos_len);
		mp->b_wptr += qos_len;
		mp->b_cont = dp;
		putnext(m2->wq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); return(-EBUSY);
}
/*
 *  N_EXDATA_REQ
 *  ---------------------------------------------
 */
static int
n_exdata_req(m2pa_t *m2, void *qos_ptr, size_t qos_len, mblk_t *dp)
{
	mblk_t *mp;
	N_exdata_req_t *p;
	if ( bcanputnext(m2->wq, 1) ) {
	if ( (mp = allocb(sizeof(*p)+qos_len, BPRI_MED)) ) {
		mp->b_datap->db_type = M_PROTO;
		p = ((N_exdata_req_t *)mp->b_wptr)++;
		p->PRIM_type		= N_EXDATA_REQ;
		bcopy(qos_ptr, mp->b_wptr, qos_len);
		mp->b_wptr += qos_len;
		mp->b_cont = dp;
		putnext(m2->wq, mp);
		return(0);
	} rare();   return(-ENOBUFS);
	} seldom(); 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(m2pa_t *m2, size_t plen)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
	if ( (mp = allocb(2*sizeof(uint32_t)+plen, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_PROVING_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(2*sizeof(uint32_t)+plen);
		fixme(("We should include a test pattern.\n"));
		bzero(mp->b_wptr, plen);
		mp->b_wptr += plen;
		if ( !(err = n_data_req(m2, 0, &qos, sizeof(qos), mp)) )
			return(0);
		freeb(mp);
		return(err);
	}
	return(-ENOBUFS);
}
#endif

#define	M2PA_STATUS_OUT_OF_SERVICE	(__constant_htonl(0))	/* XXX */
#define M2PA_STATUS_ALIGNMENT		(__constant_htonl(1))
#define M2PA_STATUS_PROVING_NORMAL	(__constant_htonl(2))
#define M2PA_STATUS_PROVING_EMERGENCY	(__constant_htonl(3))
#define M2PA_STATUS_IN_SERVICE		(__constant_htonl(4))
#define M2PA_STATUS_PROCESSOR_OUTAGE	(__constant_htonl(5))
#define M2PA_STATUS_PROCESSOR_ENDED	(__constant_htonl(6))
#define M2PA_STATUS_BUSY		(__constant_htonl(7))
#define M2PA_STATUS_BUSY_ENDED		(__constant_htonl(8))

/*
 *  M2PA SEND STATUS
 *  ---------------------------------------------
 */
static int
m2pa_send_status(m2pa_t *m2, uint32_t status)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_STATUS_STREAM, };
#if 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;
		default:				r = "(unknown)";	 break;
	}
	ptrace(("%x, sending STATUS status = %s\n",(uint)m2,r));
	}
#endif
	if ( (mp = allocb(3*sizeof(uint32_t), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_STATUS_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(3*sizeof(uint32_t));
		*((uint32_t *)mp->b_wptr)++ = status;
		if ( !(err = n_data_req(m2, 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(m2pa_t *m2, ulong count)
{
	int err;
	mblk_t *mp;
	N_qos_sel_data_sctp_t qos = { N_QOS_SEL_DATA_SCTP, M2PA_PPI, M2PA_DATA_STREAM, };
//	ptrace(("%x, sending DATACK\n",(uint)m2));
	if ( (mp = allocb(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(m2, &qos, sizeof(qos), mp)) )
			return(0);
		assert(mp->b_datap->db_ref);
		freeb(mp);
		return(err);
	}
	return(-ENOBUFS);

}
/*
 *  M2PA SEND DATA
 *  ---------------------------------------------
 */
static int
m2pa_send_data(m2pa_t *m2, mblk_t *dp)
{
	int err;
	mblk_t *mp, *db;
	ulong rcpt = m2->version<M2PA_VERSION_DRAFT4?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)m2,dlen));
	if ( !(db = dupmsg(dp)) )
		return(-ENOBUFS);
	if ( (mp = allocb(2*sizeof(uint32_t), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_DATA;
		*((uint32_t *)mp->b_wptr)++ = M2PA_DATA_MESSAGE;
		*((uint32_t *)mp->b_wptr)++ = __constant_htonl(dlen + 2*sizeof(uint32_t));
		mp->b_cont = db;
		pullupmsg(mp, -1);
		if ( !(err = n_data_req(m2, 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);
}
/*
 *  M2PA SEND
 *  ---------------------------------------------
 */
static int
m2pa_send(m2pa_t *m2, mblk_t *dp)
{
	int err;
	ulong rcpt = N_RC_FLAG;
	uint status;
	uint acks = 0;
	size_t dlen = dp->b_wptr - dp->b_rptr;
	size_t plen = 0;
	unsigned char *rptr = dp->b_rptr;

	if ( m2->version >= M2PA_VERSION_DRAFT4 )
	{
		rcpt = 0;
		plen = 2*sizeof(uint32_t);
	}
	if ( m2->options.popt & SS7_POPT_XSN )
	{
		if ( dlen < 6 ) { rare(); return(-EMSGSIZE); }
		switch ( dlen )
		{
			case 6:	    status = M2PA_FISU;
#ifdef M2PA_TX_COMPRESSION
				    if ( status == m2->tx_status &&
						    m2->version < M2PA_VERSION_DRAFT4 )
					    return(0);
#endif
				    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];
#ifdef M2PA_TX_COMPRESSION
				    if ( status == m2->tx_status ) return(0);
#endif
				    break;
			case 8:	    status = dp->b_rptr[7];
#ifdef M2PA_TX_COMPRESSION
				    if ( status == m2->tx_status ) return(0);
#endif
				    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;
#ifdef M2PA_TX_COMPRESSION
				    if ( status == m2->tx_status &&
						    m2->version < M2PA_VERSION_DRAFT4 )
					    return(0);
#endif
				    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];
#ifdef M2PA_TX_COMPRESSION
				    if ( status == m2->tx_status ) return(0);
#endif
				    break;
			case 5:	    status = dp->b_rptr[4];
#ifdef M2PA_TX_COMPRESSION
				    if ( status == m2->tx_status ) return(0);
#endif
				    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(m2, 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(m2, M2PA_STATUS_IN_SERVICE)) )
					return(err);
				break;
			}
			if ( m2->version < M2PA_VERSION_DRAFT4 )
				break;
			if ( acks )
			{
				if ( (err = m2pa_send_datack(m2, acks)) )
					return(err);
				m2->bsna = m2->bsnt;
			}
			break;
		case M2PA_LSSU_SIO:
			if ( (err = m2pa_send_status(m2, M2PA_STATUS_ALIGNMENT)) )
				return(err);
			break;
		case M2PA_LSSU_SIN:
			if ( (err = m2pa_send_status(m2, M2PA_STATUS_PROVING_NORMAL)) )
				return(err);
			break;
		case M2PA_LSSU_SIE:
			if ( (err = m2pa_send_status(m2, M2PA_STATUS_PROVING_EMERGENCY)) )
				return(err);
			break;
		case M2PA_LSSU_SIOS:
			if ( (err = m2pa_send_status(m2, M2PA_STATUS_OUT_OF_SERVICE)) )
				return(err);
			break;
		case M2PA_LSSU_SIPO:
			if ( (err = m2pa_send_status(m2, M2PA_STATUS_PROCESSOR_OUTAGE)) )
				return(err);
			break;
		case M2PA_LSSU_SIB:
			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(m2, M2PA_STATUS_BUSY)) )
					return(err);
			}
			break;
		case M2PA_MSU:
			if ( (err = m2pa_send_data(m2, dp)) )
			{
				dp->b_rptr = rptr;
				return(err);
			}
			if ( acks )
			{
				if ( (err = m2pa_send_datack(m2, 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(m2pa_t *m2, ulong count)
{
	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 = allocb(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 = allocb(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(m2, mp)) )
			return(0);
//		if ( !(err = sdl_daedr_received_bits_ind(m2, 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(m2pa_t *m2, ulong count, uint8_t status)
{
	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)m2, r));
	}
#endif
	if ( m2->options.popt & SS7_POPT_XSN )
	{
		ptrace(("*******ERRROR: I'm not testing XSN!\n"));
		if ( (mp = allocb(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 = allocb(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(m2, mp)) )
			return(0);
//		if ( !(err = sdl_daedr_received_bits_ind(m2, 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(m2pa_t *m2, mblk_t *dp)
{
	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)m2, 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 = allocb(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 = allocb(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(m2, 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 int
m2pa_deliver_now(m2pa_t *m2, uint8_t status, ulong count, mblk_t *mp)
{
	int err;
	switch ( status )
	{
		case M2PA_FISU:
			if ( (err = m2pa_deliver_fisu(m2, count)) )
				return(err);
			break;
		case M2PA_MSU:
			if ( (err = m2pa_deliver_msu(m2, 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(m2, count, status)) )
				return(err);
			break;
	}
	return(0);
}
static int
m2pa_deliver(m2pa_t *m2, uint8_t status, mblk_t *mp)
{
	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(m2, m2->rx_status, m2->count, NULL)) )
				return(err);
			m2->count = 0;
		}
	}
#endif
	if ( (err = m2pa_deliver_now(m2, status, 1, mp)) )
		return(err);
	m2->rx_status = status;
	return(0);
}

static void
m2pa_rx_sib_timeout(caddr_t data)
{
	m2pa_t *m2 = (m2pa_t *)data;
	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)m2, M2PA_RX_SIB_TIME);
	putctl1(m2->rq, M_CTL, M2PA_EVENT_RX_SIB_TIMEOUT);
	return;
}

/*
 *  M2PA RECV DATACK
 *  ---------------------------------------------
 */
static int
m2pa_recv_datack(m2pa_t *m2, mblk_t *mp, size_t mlen)
{
	int err;
	uint bsnr = m2->bsnr;
	(void)mp;

	if ( m2->version >= M2PA_VERSION_DRAFT4 ) {
	if ( mlen >= sizeof(uint32_t) ) {
		m2->bsnr += ntohl(*((uint32_t *)mp->b_rptr)++);
		if ( (err = m2pa_deliver(m2, M2PA_FISU, NULL)) ) {
			m2->bsnr = bsnr;
			return(err);
		}
		return(0);
	} rare();   return(-EMSGSIZE);
	} seldom(); return(-EPROTO);
}
/*
 *  M2PA RECV DATA
 *  ---------------------------------------------
 */
static int
m2pa_recv_data(m2pa_t *m2, mblk_t *mp, size_t mlen)
{
	int err;
	uint fsnr = m2->fsnr++;

	if ( (err = m2pa_deliver(m2, M2PA_MSU, mp)) )
	{
		m2->fsnr = fsnr;
		return(err);
	}
	return(0);
}

/*
 *  M2PA RECV STATUS
 *  ---------------------------------------------
 */
static int
m2pa_recv_status(m2pa_t *m2, mblk_t *mp, size_t mlen)
{
	int err;
	uint status;
	if ( mlen == 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:    status = M2PA_LSSU_SIOS;	break;
			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)m2, 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;
			default: rare(); return(-EPROTO);
		}
		if ( (err = m2pa_deliver(m2, status, NULL)) )
			return(err);
		return(0);

	} rare(); return (-EMSGSIZE);
}
/*
 *  M2PA RECV PROVING
 *  ---------------------------------------------
 */
static int
m2pa_recv_proving(m2pa_t *m2, mblk_t *mp, size_t mlen)
{
	int err;
	if ( mlen > 0 )
	{
#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(m2, 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(m2, status, NULL)) )
			return(err);
#endif
	}
	return(0);
}
/*
 *  M2PA RECV
 *  ---------------------------------------------
 */
static int
m2pa_recv_msg(m2pa_t *m2, mblk_t *mp)
{
	size_t mlen;
	uint32_t type;

	if ( mp->b_wptr >= mp->b_rptr + 2*sizeof(uint32_t) ) {
	type = ((uint32_t *)mp->b_rptr)[0];
	mlen = ntohl(((uint32_t *)mp->b_rptr)[1]);
	if ( mp->b_wptr >= mp->b_rptr + mlen ) {
	mp->b_rptr += 2*sizeof(uint32_t);
	mlen -= 2*sizeof(uint32_t);
	switch ( type ) {

		case M2PA_DATA_MESSAGE:	    return m2pa_recv_data   (m2, mp, mlen);
		case M2PA_STATUS_MESSAGE:   return m2pa_recv_status (m2, mp, mlen);
		case M2PA_PROVING_MESSAGE:  return m2pa_recv_proving(m2, mp, mlen);
		case M2PA_DATACK_MESSAGE:   return m2pa_recv_datack (m2, mp, mlen);

	} rare(); return(-EPROTO);
	} rare(); return(-EMSGSIZE);
	} rare(); return(-EPROTO);
}

/*
 *  =========================================================================
 *
 *  SDL Provider (M2PA) <- SDL User Primitives
 *
 *  =========================================================================
 */
/*
 *  LMI_INFO_REQ
 *  ---------------------------------------------
 */
static int
lmi_info_req(m2pa_t *m2, mblk_t *mp)
{
	(void)mp;
	return lmi_info_ack(m2);
}
/*
 *  LMI_ATTACH_REQ
 *  ---------------------------------------------
 */
static int
lmi_attach_req(m2pa_t *m2, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_attach_req_t *p = (lmi_attach_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( m2->state == LMI_UNATTACHED ) { m2->state = LMI_ATTACH_PENDING;

		lmi_error_ack(m2, LMI_ATTACH_REQ, LMI_NOTSUPP);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(m2, LMI_ATTACH_REQ, err);
}
/*
 *  LMI_DETACH_REQ
 *  ---------------------------------------------
 */
static int
lmi_detach_req(m2pa_t *m2, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_detach_req_t *p = (lmi_detach_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( m2->state == LMI_DISABLED ) { m2->state = LMI_DETACH_PENDING;

		lmi_error_ack(m2, LMI_DETACH_REQ, LMI_NOTSUPP);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(m2, LMI_DETACH_REQ, err);
}
/*
 *  LMI_ENABLE_REQ
 *  ---------------------------------------------
 */
static int
lmi_enable_req(m2pa_t *m2, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_enable_req_t *p = (lmi_enable_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( m2->state == LMI_DISABLED ) { m2->state = LMI_ENABLE_PENDING;

		return lmi_enable_con(m2);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(m2, LMI_ENABLE_REQ, err);
}
/*
 *  LMI_DISABLE_REQ
 *  ---------------------------------------------
 */
static int
lmi_disable_req(m2pa_t *m2, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_disable_req_t *p = (lmi_disable_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {
	if ( 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(m2, M2PA_STATUS_OUT_OF_SERVICE);
		}
		m2->state = LMI_DISABLE_PENDING;

		return lmi_disable_con(m2);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(m2, LMI_DISABLE_REQ, err);
}
/*
 *  LMI_OPTMGMT_REQ
 *  ---------------------------------------------
 */
static int
lmi_optmgmt_req(m2pa_t *m2, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lmi_optmgmt_req_t *p = (lmi_optmgmt_req_t *)mp->b_rptr;
	do {
	if ( mlen >= sizeof(*p) ) {

		lmi_error_ack(m2, LMI_OPTMGMT_REQ, LMI_NOTSUPP);

	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); seldom(); return lmi_error_ack(m2, 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(m2pa_t *m2, int err)
{
	mblk_t *mp;
	switch ( err )
	{
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
		case -ENOBUFS:
			seldom(); return(err);
		case 0:
		case 1:
		case 2:
			never(); return(err);
	}
	if ( (mp = allocb(2, BPRI_HI)) )
	{
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = abs(err);
		*(mp->b_wptr)++ = abs(err);
		putnext(m2->rq, mp);
		return(0);
	}
	rare(); return(-ENOBUFS);
}
static int
sdl_write(m2pa_t *m2, mblk_t *mp)
{
	int err;
	do {
	if ( m2->state != LMI_DISABLED ) {
	if ( m2->state == LMI_ENABLED ) {

		if ( m2->flags & M2PA_FLAG_TX_ENABLED )
		{
			if ( (err = m2pa_send(m2, mp)) )
				break;
			return(0);
		}
		rare();
		return(0);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state   */
	} seldom(); return(0);			    /* ingore data in DISABLED state	    */
	} while(0); rare(); return m_error_reply(m2, err); /* XXX */
}
/*
 *  SDL_DAEDT_TRAMSMISSION_REQ
 *  ---------------------------------------------
 */
static int
sdl_daedt_xmit_req(m2pa_t *m2, mblk_t *mp)
{
	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;
	do {
	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(m2, dp)) )
				break;
			return(0);
		}
		rare();
		return(0);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state   */
	} seldom(); return(0);		    break;  /* ingore data in DISABLED state	    */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short		    */
	} rare();   err = -EFAULT;	    break;  /* no M_DATA blocks			    */
	} while(0); rare(); return m_error_reply(m2, err); /* XXX */
}
/*
 *  SDL_DAEDT_START_REQ
 *  ---------------------------------------------
 */
static int
sdl_daedt_start_req(m2pa_t *m2, mblk_t *mp)
{
	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;
	do {
	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);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); rare(); return m_error_reply(m2, err); /* XXX */
}
/*
 *  SDL_DAEDR_START_REQ
 *  ---------------------------------------------
 */
static int
sdl_daedr_start_req(m2pa_t *m2, mblk_t *mp)
{
	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;
	do {
	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);

	} seldom(); err = LMI_OUTSTATE;	    break;  /* would place interface out of state */
	} seldom(); err = LMI_PROTOSHORT;   break;  /* M_PROTO block too short	    */
	} while(0); rare(); return m_error_reply(m2, err); /* XXX */
}
/*
 *  =========================================================================
 *
 *  NPI User (M2PA) <- NPI Provider (SCTP) Primitives
 *
 *  =========================================================================
 */
/*
 *  N_CONN_IND
 *  ---------------------------------------------
 */
static int
n_conn_ind(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_CONN_CON
 *  ---------------------------------------------
 */
static int
n_conn_con(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_DISCON_IND
 *  ---------------------------------------------
 */
static int
n_discon_ind(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_DATA_IND
 *  ---------------------------------------------
 */
static int
n_data_ind(m2pa_t *m2, mblk_t *mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_data_ind_t *p = (N_data_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );

	if ( (dp = mp->b_cont) ) {
	if ( m2->state == LMI_ENABLED ) {

		if ( m2->flags & M2PA_FLAG_RX_ENABLED )
		{
			if ( (err = m2pa_recv_msg(m2, dp)) )
				return(err);
			mp->b_cont = NULL; /* absorbed data */
		}
		return(0);

	} rare(); return(-EPROTO);	/* ignore data in other states	*/
	} rare(); return(-EFAULT);
}
/*
 *  N_EXDATA_IND
 *  ---------------------------------------------
 */
static int
n_exdata_ind(m2pa_t *m2, mblk_t *mp)
{
	int err;
	mblk_t *dp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_exdata_ind_t *p = (N_exdata_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );

	if ( (dp = mp->b_cont) ) {
	if ( m2->state == LMI_ENABLED ) {

		if ( m2->flags & M2PA_FLAG_RX_ENABLED )
		{
			if ( (err = m2pa_recv_msg(m2, dp)) )
				return(err);
			mp->b_cont = NULL; /* absorbed data */
		}
		return(0);

	} rare(); return(-EPROTO);	/* ignore data in other states */
	} rare(); return(-EFAULT);
}
/*
 *  N_DATACK_IND
 *  ---------------------------------------------
 */
static int
n_datack_ind(m2pa_t *m2, mblk_t *mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_datack_ind_t *p = (N_datack_ind_t *)mp->b_rptr;
	ensure( mlen >= sizeof(*p), return(-EFAULT) );

	if ( m2->version >= M2PA_VERSION_DRAFT4 ) {
		if ( m2->sacks ) {
			m2->sacks--;
			return(0);
		}
		if ( m2->dacks ) {
			uint bsnr = m2->bsnr;
			m2->bsnr++;
			if ( (err = m2pa_deliver(m2, M2PA_FISU, NULL)) ) {
				m2->bsnr = bsnr;
				return(err);
			}
			m2->dacks--;
			return(0);
		}
	} seldom(); return(-EPROTO);
}
/*
 *  N_INFO_ACK
 *  ---------------------------------------------
 */
static int
n_info_ack(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_BIND_ACK
 *  ---------------------------------------------
 */
static int
n_bind_ack(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_OK_ACK
 *  ---------------------------------------------
 */
static int
n_ok_ack(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_ERROR_ACK
 *  ---------------------------------------------
 */
static int
n_error_ack(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_RESET_IND
 *  ---------------------------------------------
 */
static int
n_reset_ind(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}
/*
 *  N_RESET_CON
 *  ---------------------------------------------
 */
static int
n_reset_con(m2pa_t *m2, mblk_t *mp)
{
	(void)m2;
	(void)mp;
//	fixme(("Do something here...\n"));
	return(0);
	m2->state = LMI_DISABLED;
	return lmi_error_ind(m2, LMI_DISC);
}

/*
 *  =========================================================================
 *
 *  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_t *)q->q_ptr;
	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_t *)q->q_ptr;
	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_t *)q->q_ptr;
	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_t *)q->q_ptr;
	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_t *)q->q_ptr;
	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_t *)q->q_ptr;
	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;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	ulong oldstate = m2->state;
	switch ( (prim = *((ulong *)mp->b_rptr)) )
	{
		case LMI_INFO_REQ:		 rtn = lmi_info_req	   (m2, mp); break;
		case LMI_ATTACH_REQ:		 rtn = lmi_attach_req	   (m2, mp); break;
		case LMI_DETACH_REQ:		 rtn = lmi_detach_req	   (m2, mp); break;
		case LMI_ENABLE_REQ:		 rtn = lmi_enable_req	   (m2, mp); break;
		case LMI_DISABLE_REQ:		 rtn = lmi_disable_req	   (m2, mp); break;
		case LMI_OPTMGMT_REQ:		 rtn = lmi_optmgmt_req	   (m2, mp); break;
		case SDL_DAEDT_TRANSMISSION_REQ: rtn = sdl_daedt_xmit_req  (m2, mp); break;
		case SDL_DAEDT_START_REQ:	 rtn = sdl_daedt_start_req (m2, mp); break;
		case SDL_DAEDR_START_REQ:	 rtn = sdl_daedr_start_req (m2, mp); break;
		default:		rtn = -EOPNOTSUPP;		  break;
	}
	if ( rtn < 0 ) { seldom(); m2->state = oldstate; }
	return(rtn);
}
static int
m2pa_r_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	ulong prim;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	switch ( (prim = *((ulong *)mp->b_rptr)) )
	{
		case N_CONN_IND:	rtn = n_conn_ind	(m2, mp); break;
		case N_CONN_RES:	rtn = n_conn_con	(m2, mp); break;
		case N_DISCON_IND:	rtn = n_discon_ind	(m2, mp); break;
		case N_DATA_IND:	rtn = n_data_ind	(m2, mp); break;
		case N_EXDATA_IND:	rtn = n_exdata_ind	(m2, mp); break;
		case N_DATACK_IND:	rtn = n_datack_ind	(m2, mp); break;
		case N_INFO_ACK:	rtn = n_info_ack	(m2, mp); break;
		case N_BIND_ACK:	rtn = n_bind_ack	(m2, mp); break;
		case N_OK_ACK:		rtn = n_ok_ack		(m2, mp); break;
		case N_ERROR_ACK:	rtn = n_error_ack	(m2, mp); break;
		case N_RESET_IND:	rtn = n_reset_ind	(m2, mp); break;
		case N_RESET_CON:	rtn = n_reset_con	(m2, 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;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	if ( (err = sdl_write(m2, mp)) )
		return(err);
	return(1); /* message absorbed */
}
static int
m2pa_r_data(queue_t *q, mblk_t *mp)
{
	int err;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	if ( (err = m2pa_recv_msg(m2, mp)) )
		return(err);
	return(1);  /* message absorbed */
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling (Events)
 *
 *  -------------------------------------------------------------------------
 */
static int
m2pa_r_ctl(queue_t *q, mblk_t *mp)
{
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	switch ( *mp->b_rptr )
	{
		case M2PA_EVENT_TX_SIB_TIMEOUT:
			m2->flags &= ~M2PA_FLAG_TX_BUSY;
			m2pa_send_status(m2, M2PA_STATUS_BUSY_ENDED);
			return(0);
		case M2PA_EVENT_RX_SIB_TIMEOUT:
			m2pa_deliver(m2, 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(1);
		}
		ret = -EOPNOTSUPP;
		break;
	}
	if ( ret >= 0 )
	{
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval	= 0;
	}
	else
	{
		seldom();
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval	= -1;
	}
	qreply(q, mp);
	return(1);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static int
m2pa_r_error(queue_t *q, mblk_t *mp)
{
	putnext(q, mp);
	return(1);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  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);
	}
	putnext(q, mp);
	return(1);
}
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);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  Other messages (e.g. M_IOCACK)
 *
 *  -------------------------------------------------------------------------
 */
static int
m2pa_m_other(queue_t *q, mblk_t *mp)
{
	rare();
	if ( mp->b_datap->db_type >= QPCTL || bcanputnext(q, mp->b_band) )
	{
		putnext(q, mp);
		return(1);
	}
	else return(-EBUSY);
}
/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */

/*
 *  SDL Write Put and Service
 */
static INT
m2pa_wput(queue_t *q, mblk_t *mp)
{
	int rtn;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( m2, return((INT)(-EFAULT)) );
	ensure( mp, return((INT)(-EFAULT)) );
	if ( mp->b_datap->db_type < QPCTL && q->q_count )
	{
		seldom();
		putq(q, mp);
		return(INT)(0);
	}
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	rtn = m2pa_w_data (q, mp); break;
		case M_PROTO:
		case M_PCPROTO:	rtn = m2pa_w_proto(q, mp); break;
		case M_FLUSH:	rtn = m2pa_w_flush(q, mp); break;
		case M_IOCTL:	rtn = m2pa_w_ioctl(q, mp); break;
		default:	rtn = m2pa_m_other(q, mp); break;
	}
	switch ( rtn )
	{
		case 0:	assert(mp->b_datap->db_ref);
			freemsg(mp);
		case 1: break;
		case 2: assert(mp->b_datap->db_ref);
			freeb(mp);
			break;
		case -ENOBUFS:
			/* should set up bufcall */
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			seldom();
			putq(q, mp);
			return(INT)(0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			return(INT)(rtn);
	}
	return(INT)(0);
}
static INT
m2pa_wsrv(queue_t *q)
{
	int rtn;
	mblk_t *mp;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( m2, return((INT)(-EFAULT)) );
	while ( (mp = getq(q)) )
	{
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:	rtn = m2pa_w_data (q, mp); break;
			case M_PROTO:
			case M_PCPROTO:	rtn = m2pa_w_proto(q, mp); break;
			case M_FLUSH:	rtn = m2pa_w_flush(q, mp); break;
			case M_IOCTL:	rtn = m2pa_w_ioctl(q, mp); break;
			default:	rtn = m2pa_m_other(q, mp); break;
		}
		switch ( rtn )
		{
			case 0: assert(mp->b_datap->db_ref);
				freemsg(mp);
			case 1:	continue;
			case 2: assert(mp->b_datap->db_ref);
				freeb(mp);
				continue;
			case -ENOBUFS:
				/* should set up bufcall */
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				seldom();
				if ( mp->b_datap->db_type < QPCTL )
				{
					putbq(q, mp);
					return(INT)(0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
		}
	}
	return(INT)(0);
}

/*
 *  SCTP Read Put and Service
 */
static INT
m2pa_rput(queue_t *q, mblk_t *mp)
{
	int rtn;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( m2, return((INT)(-EFAULT)) );
	ensure( mp, return((INT)(-EFAULT)) );
	if ( mp->b_datap->db_type < QPCTL && q->q_count )
	{
		seldom();
		putq(q, mp);
		return(INT)(0);
	}
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	rtn = m2pa_r_data (q, mp); break;
		case M_PROTO:
		case M_PCPROTO:	rtn = m2pa_r_proto(q, mp); break;
		case M_CTL:	rtn = m2pa_r_ctl  (q, mp); break;
		case M_ERROR:	rtn = m2pa_r_error(q, mp); break;
		case M_FLUSH:	rtn = m2pa_r_flush(q, mp); break;
		default:	rtn = m2pa_m_other(q, mp); break;
	}
	switch ( rtn )
	{
		case 0: assert(mp->b_datap->db_ref);
			freemsg(mp);
		case 1: break;
		case 2: assert(mp->b_datap->db_ref);
			freeb(mp);
			break;
		case -ENOBUFS:
			/* should set up bufcall */
		case -EBUSY:
		case -EAGAIN:
		case -ENOMEM:
			seldom();
			putq(q, mp);
			return(INT)(0);
		default:
			rare();
			ptrace(("Error = %d\n", rtn));
			assert(mp->b_datap->db_ref);
			freemsg(mp);
			return(INT)(rtn);
	}
	return(INT)(0);
}
static INT
m2pa_rsrv(queue_t *q)
{
	int rtn;
	mblk_t *mp;
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	ensure( q,  return((INT)(-EFAULT)) );
	ensure( m2, return((INT)(-EFAULT)) );
	while ( (mp = getq(q)) )
	{
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:	rtn = m2pa_r_data (q, mp); break;
			case M_PROTO:
			case M_PCPROTO:	rtn = m2pa_r_proto(q, mp); break;
			case M_CTL:	rtn = m2pa_r_ctl  (q, mp); break;
			case M_ERROR:	rtn = m2pa_r_error(q, mp); break;
			case M_FLUSH:	rtn = m2pa_r_flush(q, mp); break;
			default:	rtn = m2pa_m_other(q, mp); break;
		}
		switch ( rtn )
		{
			case 0: assert(mp->b_datap->db_ref);
				freemsg(mp);
			case 1: continue;
			case 2: assert(mp->b_datap->db_ref);
				freeb(mp);
				continue;
			case -ENOBUFS:
				/* should set up bufcall */
			case -EBUSY:
			case -EAGAIN:
			case -ENOMEM:
				seldom();
				if ( mp->b_datap->db_type < QPCTL )
				{
					putbq(q, mp);
					return(INT)(0);
				}
			default:
				rare();
				ptrace(("Error = %d\n", rtn));
				assert(mp->b_datap->db_ref);
				freemsg(mp);
				continue;
		}
	}
	return(INT)(0);
}

/*
 *  =========================================================================
 *
 *  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))) )
	if ( !(m2pa_cachep = kmem_cache_create( "m2pa_cachep", sizeof(m2pa_t),
					0, SLAB_HWCACHE_ALIGN, NULL, NULL)) )
		panic(__FUNCTION__ ":Cannot alloc m2pa_cachep.\n");
	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;
	}
	return(m2);
}
static void
m2pa_free_priv(queue_t *q)
{
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	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;
}
