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

 @(#) m2pa_sdt.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_sdt.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";

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

typedef void (*bufcall_fnc_t)(long);

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info m2pa_info =
{
	0,		/* Module ID number		*/
	"m2pa-sdt",	/* 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_info,	/* 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_info,	/* Information			*/
	NULL		/* Statistics			*/
};

static INT t_rput (queue_t *, mblk_t *);
static INT t_rsrv (queue_t *);

static struct streamtab m2pa_info =
{
	&m2pa_rinit,	/* Upper read  queue		*/
	&m2pa_winit,	/* Upper write queue		*/
	&t_rinit,	/* Lower read  queue		*/
	&t_winit	/* Lower write queue		*/
};

/*
 *  =========================================================================
 *
 *  M2PA Private Structure
 *
 *  =========================================================================
 */
typedef struct m2pa {
	struct m2pa	*next;
	queue_t		*rq;
	queue_t		*wq;
	uint		state;		/* SDT interface state		*/
	uint		version;	/* version execting		*/
	uint		flags;		/* interface flags		*/
	ulong		token;		/* my bind token		*/

	lmi_option_t	options;	/* protocol and variant options	*/
	sdt_config_t	sdt_conf;	/* SDT configuration options	*/
	sdl_config_t	sdl_conf;	/* SDL configuration options	*/
	dev_config_t	dev_conf;	/* DEV configuration options	*/
} m2pa_t;

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

/*
 *  =========================================================================
 *
 *  SDT Provider (M2PA) -> DTL 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
 *  ---------------------------------------------
 */
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);
}
/*
 *  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
 *  ---------------------------------------------
 */
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);
}
/*
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
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);
}
/*
 *  SDT_RC_SIGNAL_UNIT_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_signal_unit_ind(void)
{
	mblk_t *mp;
	sdt_rc_signal_unit_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_signal_unit_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_RC_SIGNAL_UNIT_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_RC_CONGESTION_ACCEPT_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_congestion_accept_ind(void)
{
	mblk_t *mp;
	sdt_rc_congestion_accept_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_congestion_accept_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_RC_CONGESTION_ACCEPT_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_RC_CONGESTION_DISCARD_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_congestion_discard_ind(void)
{
	mblk_t *mp;
	sdt_rc_congestion_discard_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_congestion_discard_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_RC_CONGESTION_DISCARD_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_RC_NO_CONGESTION_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_no_congestion_ind(void)
{
	mblk_t *mp;
	sdt_rc_no_congestion_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_no_congestion_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_RC_CONGESTION_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_IAC_CORRECT_SU_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_iac_correct_su_ind(void)
{
	mblk_t *mp;
	sdt_iac_correct_su_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_iac_correct_su_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_IAC_CORRECT_SU_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_IAC_ABORT_PROVING_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_iac_abort_proving_ind(void)
{
	mblk_t *mp;
	sdt_iac_abort_proving_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_iac_abort_proving_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_IAC_ABORT_PROVING_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_LSC_LINK_FAILURE_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_lsc_link_failure_ind(void)
{
	mblk_t *mp;
	sdt_lsc_link_failure_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_lsc_link_failure_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_LSC_LINK_FAILURE_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SDT_TXC_TRANSMISSION_REQUEST_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_txc_transmission_request_ind(void)
{
	mblk_t *mp;
	sdt_txc_transmission_request_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_txc_transmission_request_ind_t *)mp->b_wptr;
		p->sdt_primitive    = SDT_TXC_TRANSMISSION_REQUEST_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  =========================================================================
 *
 *  M2PA SL-Provider --> T-Provider (SCTP) Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */
/*
 *  T_INFO_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_info_req(void)
{
	mblk_t *mp;
	struct T_info_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_info_req *)mp->b_wptr;
		p->PRIM_type	= T_INFO_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_ADDR_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_addr_req(void)
{
	mblk_t *mp;
	struct T_addr_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_addr_req *)mp->b_wptr;
		p->PRIM_type	= T_ADDR_REQ;
		p->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_BIND_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_bind_req(add_ptr, add_len, cons)
	const caddr_t add_ptr;
	const size_t add_len;
	const uint cons;
{
	mblk_t *mp;
	struct T_bind_req *p;

	if ( (mp = allocb(sizeof(*p)+add_len, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_bind_req *)mp->b_wptr;
		p->PRIM_type	    = T_BIND_REQ;
		p->ADDR_length	    = add_len;
		p->ADDR_offset	    = add_len ? sizeof(*p) : 0;
		p->CONIND_number    = cons;
		mp->b_wptr += sizeof(*p);
		bcopy(add_ptr, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	return(mp);
}
/*
 *  T_UNBIND_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_unbind_req(void)
{
	mblk_t *mp;
	struct T_unbind_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_unbind_req *)mp->b_wptr;
		p->PRIM_type	= T_UNBIND_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_OPTMGMT_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_optmgmt_req(opt_ptr, opt_len, flags)
	const caddr_t opt_ptr;
	const size_t opt_len;
	const uint flags;
{
	mblk_t *mp;
	struct T_optmgnt_req *p;
	if ( (mp = allocb(sizeof(*p)+opt_len, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_optmgmt_req *)mp->b_wptr;
		p->PRIM_type	= T_OPMGMT_REQ;
		p->OPT_length	= opt_len;
		p->OPT_offset	= sizeof(*p);
		p->MGMT_flags	= flags;
		mp->b_wptr += sizeof(*p);
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
	}
	return(mp);
}
/*
 *  T_CONN_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_conn_req(dst_ptr, dst_len, opt_ptr, opt_len)
	const caddr_t dst_ptr;
	const size_t dst_len;
	const caddr_t opt_ptr;
	const size_t opt_len;
{
	mblk_t *mp;
	struct T_conn_req *p;

	if ( (mp = allocb(sizeof(*p)+dst_len+opt_len, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_conn_req *)mp->b_ptpr;
		p->PRIM_type	= T_CONN_REQ;
		p->DEST_length	= dst_len;
		p->DEST_offset	= dst_len ? sizeof(*p) : 0;
		p->OPT_length	= opt_len;
		p->OPT_offset	= opt_len ? sizeof(*p)+dst_len : 0;
		mp->b_wptr += sizeof(*p);
		bcopy(dst_ptr, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
	}
	return(mp);
}
/*
 *  T_CONN_RES
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_conn_res(aq, opt_ptr, opt_len, seq)
	const queue_t *aq;
	const caddr_t opt_ptr;
	const size_t opt_len;
	const uint seq;
{
	mblk_t *mp;
	struct T_conn_res *p;
	if ( (mp = allocb(sizeof(*p)+opt_len, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_conn_res *)mp->b_wptr;
		p->PRIM_type	= T_CONN_RES;
		p->QUEUE_ptr	= q;
		p->OPT_length	= opt_len;
		p->OPT_offset	= sizeof(*p);
		p->SEQ_number	= seq;
		mp->b_wptr += sizeof(*p);
		bopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b-wptr += opt_len;
	}
	return(mp);
}
/*
 *  T_DISCON_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_discon_req(seq)
	const uint seq;
{
	mblk_t *mp;
	struct T_discon_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_discon_req *)mp->b_wptr;
		p->PRIM_type	= T_DISCON_REQ;
		p->SEQ_number	= seq;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_ORDREL_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_ordrel_req(void)
{
	mblk_t *mp;
	struct T_ordrel_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_ordrel_req *)mp->b_wptr;
		p->PRIM_type	= T_ORDREL_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_DATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_data_req(void)
{
	mblk_t *mp;
	struct T_data_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_data_req *)mp->b_wptr;
		p->PRIM_type	= T_DATA_REQ;
		p->MORE_flag	= 0;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_EXDATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_exdata_req(void)
{
	mblk_t *mp;
	struct T_exdata_ind *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_exdata_req *)mp->b_wptr;
		p->PRIM_type	= T_EXDATA_REQ;
		p->MORE_flag	= 0;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_OPTDATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_optdata_req(uint sid)
{
	mblk_t *mp;
	struct T_optdata_req *p;
	struct t_opthdr *oh;
	const size_t opt_len = PAD4(sizeof(*oh)+sizeof(u16))+PAD4(sizeof(*oh)+sizeof(u32));

	if ( (mp = allocb(sizeof(*p)+opt_len, BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_optdata_req *)mp->b_wptr;
		p->PRIM_type	= T_OPTDATA_REQ;
		p->DATA_flag	= 0;
		p->OPT_length	= opt_len;
		p->OPT_offset	= sizeof(*p);
		mp->b_wptr += sizeof(*p);

		oh = (struct t_opthdr *)mp->b_wptr;
		oh->level   = T_INET_SCTP;
		oh->name    = SCTP_SID;
		oh->length  = sizeof(*oh)+sizeof(u16);
		*((u16 *)oh->value) = sid;
		mp->b_wptr += PAD4(sizeof(*oh)+sizeof(u16));

		oh = (struct t_opthdr *)mp->b_wptr;
		oh->level   = T_INET_SCTP;
		oh->name    = SCTP_PPI;
		oh->length  = sizeof(*oh)+sizeof(u32);
		*((u32 *)oh->value) = SCTP_PPI_M2PA;
		mp->b_wptr += PAD4(sizeof(*oh)+sizeof(u32));
	}
	return(mp);
}
/*
 *  T_UNITDATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_unitdata_req(uint sid)
{
	mblk_t *mp;
	struct T_unitdata_req *p;
	struct t_opthdr *oh;
	const size_t dst_len = sizeof(u16);
	const size_t opt_len = PAD4(sizeof(*oh)+sizeof(u32));
	if ( (mp = allocb(sizeof(*p)+dst_len+opt_len)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_unitdata_req *)mp->b_wptr;
		p->PRIM_type	= T_UNITDATA_REQ;
		p->DEST_length	= dst_len;
		p->DEST_offset	= sizeof(*p);
		p->OPT_length	= 0;
		p->OPT_offset	= 0;
		mp->b_wptr += sizeof(*p);

		oh = (struct t_opthdr *)mp->b_wptr;
		oh->level   = T_INET_SCTP;
		oh->name    = SCTP_PPI;
		oh->length  = sizeof(*oh)+sizeof(u32);
		*((u32 *)oh->value) = SCTP_PPI_M2PA;
		mp->b_wptr += PAD4(sizeof(*oh)+sizeof(u32));
	}
	return(mp);
}

/*
 *  =========================================================================
 *
 *  M2PA --> M2PA Peer Primitives (Send Messages)
 *
 *  =========================================================================
 */
static inline int m2pa_send_status(queue_t *q, uint status)
{
	mblk_t *mp, *pp;
	struct m2pa_status *m;

	if ( (pp = t_optdata_req(0)) )
	{
		if ( (mp = allocb(sizeof(*m), BPRI_MED)) )
		{
			mp->b_datap->db_type = M_DATA;
			m = (struct m2pa_status *)mp->b_wptr;
			m->status   = htonl(status);
			mp->b_wptr += sizeof(*m);
			pp->b_cont = mp;

			putnext(WR(q), pp);
			return(0);
		}
		freemsg(pp);
	}
	return -ENOBUFS;
}

/*
 *  M2PA_STATUS_BUSY
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_busy(queue_t *q)
{
	return m2pa_send_status(q, M2PA_STATUS_BUSY);
}
/*
 *  M2PA_STATUS_NOT_BUSY
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_not_busy(queue_t *q)
{
	return m2pa_send_status(q, M2PA_STATUS_NOT_BUSY);
}
/*
 *  M2PA_STATUS_PROCESSOR_OUTAGE
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_processor_outage(queue_t *q)
{
	return m2pa_send_status(q, M2PA_STATUS_PROCESSOR_OUTAGE);
}
/*
 *  M2PA_STATUS_NOT_PROCESSOR_OUTAGE
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_not_processor_outage(queue_t *q)
{
	return m2pa_send_status(q, M2PA_STATUS_NOT_PROCESSOR_OUTAGE);
}
/*
 *  M2PA_STATUS_IN_SERVICE
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_in_service(queue_t *q)
{
	return m2pa_send_status(q, M2PA_STATUS_IN_SERVICE);
}
/*
 *  M2PA_SEND_DATA
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_data(queue_t *q, u16 sls, mblk_t *dp)
{
	mblk_t *mp;

	if ( (mp = t_optdata_req(sls+1)) )
	{
		mp->b_cont = dp;
		putnext(WR(q), mp);
		return(0);
	}
	return -ENOBUFS;
}
/*
 *  =========================================================================
 *
 *  M2PA State Machine TIMEOUTS
 *
 *  =========================================================================
 */

/*
 *  =========================================================================
 *
 *  M2PA Peer --> M2PA Primitives (Received Messages)
 *
 *  =========================================================================
 */
static int m2pa_recv_status(q, sdu)
	const queue_t *q;
	const mblk_t *dp;
{
	struct m2pa_status *m;

	m = (struct m2pa_status *)sdu->b_rptr;
	switch ( ntohl(m->status) )
	{
		case M2PA_STATUS_BUSY:
		{
			/*
			 *  TODO:
			 */
			freemsg(sdu);
			return(0);
		}
		case M2PA_STATUS_NOT_BUSY:
		{
			/*
			 *  TODO:
			 */
			freemsg(sdu);
			return(0);
		}
		case M2PA_STATUS_PROCESSOR_OUTAGE:
		{
			/*
			 *  TODO:
			 */
			freemsg(sdu);
			return(0);
		}
		case M2PA_STATUS_NOT_PROCESSOR_OUTAGE:
		{
			/*
			 *  TODO:
			 */
			freemsg(sdu);
			return(0);
		}
		case M2PA_STATUS_IN_SERVICE:
		{
			/*
			 *  TODO:
			 */
			freemsg(sdu);
			return(0);
		}
	}
	return -EPROTO;
}
static int m2pa_recv_data(q, sid, sdu)
	const queue_t *q;
	const uint sid;
	const mblk_t *dp;
{
	mblk_t *mp;

	if ( !sid )
		return m2pa_recv_status(q, sdu);

	if ( (mp = sl_pdu_ind()) )
	{
		linkb(mp, sdu);
		putnext(RD(q), mp);
		return(0);
	}
	return -ENOBUFS;
}

/*
 *  =========================================================================
 *
 *  T-Provider (SCTP) --> T-User (M2PA) Primitives (M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */

/*
 *  T_INFO_ACK
 */
static int t_info_ack(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_ADDR_ACK
 */
static int t_addr_ack(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_OK_ACK
 */
static int t_ok_ack(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_ERROR_ACK
 */
static int t_error_ack(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_BIND_ACK
 */
static int t_bind_ack(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_OPTMGMT_ACK
 */
static int t_optmgmt_ack(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_CONN_IND
 */
static int t_conn_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_CONN_CON
 */
static int t_conn_con(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_DATA_IND
 */
static int t_data_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_EXDATA_IND
 */
static int t_exdata_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_OPTDATA_IND
 */
static int t_optdata_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_UNITDATA_IND
 */
static int t_unitdata_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_UDERROR_IND
 */
static int t_uderror_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_DISCON_IND
 */
static int t_discon_ind(queue_t *q, mblk_t *pdu)
{
}

/*
 *  T_ORDREL_IND
 */
static int t_ordrel_ind(queue_t *q, mblk_t *pdu)
{
}

static int (*t_prim[])(queue_t *, mblk_t *) =
{
	NULL,			/*  T_CONN_REQ	    0	*/
	NULL,			/*  T_CONN_RES	    1	*/
	NULL,			/*  T_DISCON_REQ    2	*/
	NULL,			/*  T_DATA_REQ	    3	*/
	NULL,			/*  T_EXDATA_REQ    4	*/
	NULL,			/*  T_INFO_REQ	    5	*/
	NULL,			/*  T_BIND_REQ	    6	*/
	NULL,			/*  T_UNBIND_REQ    7	*/
	NULL,			/*  T_UNITDATA_REQ  8	*/
	NULL,			/*  T_OPTMGMT_REQ   9	*/
	NULL,			/*  T_ORDREL_REQ    10	*/
	&t_conn_ind,		/*  T_CONN_IND	    11	*/
	&t_conn_con,		/*  T_CONN_CON	    12	*/
	&t_discon_ind,		/*  T_DISCON_IND    13	*/
	&t_data_ind,		/*  T_DATA_IND	    14	*/
	&t_exdata_ind,		/*  T_EXDATA_IND    15	*/
	&t_info_ack,		/*  T_INFO_ACK	    16	*/
	&t_bind_ack,		/*  T_BIND_ACK	    17	*/
	&t_error_ack,		/*  T_ERROR_ACK	    18	*/
	&t_ok_ack,		/*  T_OK_ACK	    19	*/
	&t_unitdata_ind,	/*  T_UNITDATA_IND  20	*/
	&t_uderror_ind,		/*  T_UDERROR_IND   21	*/
	&t_optmgmt_ack,		/*  T_OPTMGMT_ACK   22	*/
	&t_ordrel_ind,		/*  T_ORDREL_IND    23	*/
	NULL,			/*  T_OPTDATA_REQ   24	*/
	NULL,			/*  T_ADDR_REQ	    25	*/
	&t_optdata_ind,		/*  T_OPTDATA_IND   26	*/
	&t_addr_ack		/*  T_ADDR_ACK	    27	*/
};

/*
 *  =========================================================================
 *
 *  SL-User --> SL-Provider (M2PA) Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */
static int sdt_daedt_transmission_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_daedt_start_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_daedr_start_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_aerm_start_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_aerm_stop_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_aerm_set_ti_to_tin_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_aerm_set_ti_to_tie_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_suerm_start_req(queue_t *q, mblk_t *pdu)
{
}
static int sdt_suerm_stop_req(queue_t *q, mblk_t *pdu)
{
}

static int (*sdt_prim[])(queue_t *, mblk_t *) =
{
	&sdt_daedt_transmission_req,	/* SDT_OAEDT_TRANSMISSION_REQ		*/
	&sdt_daedt_start_req,		/* SDT_DAEDT_START_REQ			*/
	&sdt_daedr_start_req,		/* SDT_DAEDR_START_REQ			*/
	&sdt_aerm_start_req,		/* SDT_AERM_START_REQ			*/
	&sdt_aerm_stop_req,		/* SDT_AERM_STOP_REQ			*/
	&sdt_aerm_set_ti_to_tin_req,	/* SDT_AERM_SET_TI_TO_TIN_REQ		*/
	&sdt_aerm_set_ti_to_tie_req,	/* SDT_AERM_SET_TI_TO_TIE_REQ		*/
	&sdt_suerm_start_req,		/* SDT_SUERM_START_REQ			*/
	&sdt_suerm_stop_req		/* SDT_SUERM_STOP_REQ			*/
};

/*
 *  =========================================================================
 *
 *  M2PA IOCTLs
 *
 *  =========================================================================
 */

static int (*m2pa_ioctl[])(queue_t *, uint, void *) =
{
};

/*
 *  =========================================================================
 *
 *  M2PA STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_data(queue_t *q, mblk_t *mp)
{
	return m2pa_send_data(q, 0, mp);
}
static inline int m2pa_r_data(queue_t *q, mblk_t *mp)
{
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	return m2pa_recv_data(q, m2->sid, mp);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_proto(queue_t *q, mblk_t *mp)
{
	int prim = ((union SDT_primitives *)(mp->b_rptr))->sdt_primitive - SDT_DSTR_FIRST;
	if ( 0 <= prim && prim <= SDT_DSTR_LAST - SDT_DSTR_FIRST )
		if ( sdt_prim[prim] )
			return (*sdt_prim[prim])(q, mp);
	return(-EOPNOTSUPP);
}
static inline int m2pa_r_proto(queue_t *q, mblk_t *mp)
{
	int prim = ((union T_primitives *)(mp->b_rptr))->type;
	if ( 0 <= prim && prim < sizeof(n_prim)/sizeof(void *) )
		if ( t_prim[prim] )
			return (*t_prim[prim])(q, mp);
	return(-EOPNOTSUPP);
}
static inline int m2pa_w_pcproto(queue_t *q, mblk_t *mp)
{
	return m2pa_w_proto(q, mp);
}
static inline int m2pa_r_pcproto(queue_t *q, mblk_t *mp)
{
	return m2pa_r_proto(q, mp);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_ctl(queue_t *q, mblk_t *mp)
{
	(void)q;
	(void)mp;
	return(-EOPNOTSUPP);
}
static inline int m2pa_r_ctl(queue_t *q, mblk_t *mp)
{
	(void)q;
	(void)mp;
	return(-EOPNOTSUPP);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_ioctl(queue_t *q, mblk_t *mp)
{
	int ret;
	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
	int cmd = iocp->ioc_cmd;
	void *arg = mp->b_cont?mp->b_cont->b_rptr:NULL;
	int type    = _IOC_TYPE(cmd);
	int nr	    = _IOC_NR(cmd);
	int size    = _IOC_SIZE(cmd);

	switch ( type ) {
	case __SID:
		switch ( cmd ) {
			case I_LINK:
			case I_PLINK:
			case I_UNLINK:
			case I_PUNLINK:
			{
				struct linkblk *lp = (struct linkblk *)arg;
				(void)lp;
			}
			default:
			case I_FDINSERT:
				ret = -EINVAL;
		}
		break;

	case M2PA_IOC_MAGIC:
		if ( iocp->ioc_count >= size )
		{
			if ( 0 <= nr && nr < sizeof(m2pa_ioctl)/sizeof(void *) )
			{
				if ( m2pa_ioctl[nr] )
					ret = (*m2pa_ioctl[nr])(q, cmd, arg);
				else
					ret = -EOPNOTSUPP;
			}
		}
		else
			return = -EINVAL;
		break;

	default:
		if ( q->q_next )
		{
			putnext(q, mp);
			return(0);
		}
		ret = -EOPNOTSUPP;
	}
	}
	mp->b_datap->db_type = ret ? M_IOCNAK : M_IOCACK;
	iocp->ioc_error = -ret;
	iocp->ioc_rval  = ret ? -1 : 0;
	qrepy(q, mp);
	return(0);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void m2pa_w_flush(queue_t *q, mblk_t *mp)
{
	if ( *mp->b_rptr & M_FLUSHW )
	{
		flushq(q, FLUSHALL );
		if ( q-q_next )
		{
			putnext(q, mp);
			return;
		}
		*mp->b_rptr &= ~FLUSHW;
	}
	if ( *mp->b_rptr & FLUSHR )
	{
		flushq(RD(q), FLUSHALL);
		qreply(q, mp);
		return;
	}
	if ( q->q_next )
	{
		putnext(q, mp);
		return;
	}
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void m2pa_r_error(queue_t *q, mblk_t *mp)
{
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	m2->zapped = 1;
	if ( q->q_next )
	{
		putnext(q, mp);
		return;
	}
}

/*
 *  =========================================================================
 *
 *  STREAMS QUEUE PUT and QUEUE SERVICE routines
 *
 *  =========================================================================
 *
 *  READ QUEUE PUT and SRV routines
 *
 *  -------------------------------------------------------------------------
 *
 *  M2PA RPUT - Message from below.
 *
 *  If the message is a priority message we attempt to process it immediately.
 *  If the message is a non-priority message, but there are no messages on the
 *  queue yet, we attempt to process it immediately.  If the message is not
 *  supported, we pass it down-queue if possible.  If the message cannot be
 *  processed immediately we place it on the queue.
 */
static INT
m2pa_rput(queue_t *q, mblk_t *mp)
{
	int err = -EOPNOTSUPP;

	if ( mp->b_datap->db_type < QPCTL && q->q_count )
	{
		putq(q, mp);
		/*
		 *  NOTE:- after placing messages on the queue here, I should
		 *  check if placing the message on the queue crosses a band
		 *  threshold for congestion accept and congestion discard.
		 *  When crossing congestion accept, I should sent busy to the
		 *  peer and notify MTP3.  When crossing congestion discard I
		 *  should notify MTP3.
		 */
		return(0);
	}
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:
			if ( (err = m2pa_r_data(q, mp)) )
				break;
			return(0);
		case M_PROTO:
			if ( (err = m2pa_r_proto(q, mp)) )
				break;
			return(0);
		case M_PCPROTO:
			if ( (err = m2pa_r_pcproto(q, mp)) )
				break;
			return(0);
		case M_CTL:
			if ( (err = m2pa_r_ctl(q, mp)) )
				break;
			return(0);
		case M_ERROR:
			m2pa_r_error(q, mp);
			return(0);
	}
	switch ( err )
	{
		case -EAGAIN:
			putq(q, mp);
			return(0);
		case -EOPNOTSUPP:
			if ( q->q_next )
			{
				putnext(q, mp);
				return(0);
			}
	}
	freemsg(mp);
	return(err);
}
/*
 *  M2PA RSRV - Queued message from below.
 *
 *  If the message is a priority message we attempt to process it immediately
 *  and without flow control.  If the message is a non-priority message and
 *  the next queue is flow controlled, we put the message back on the queue
 *  and wait.  If we cannot process a priority message immediately we cannot
 *  place it back on the queue and discard it.  We requeue non-priority
 *  messages which cannot be processed immediately.  Unrecognized messages are
 *  passed down-queue.
 */
static INT
m2pa_rsrv(queue_t *q)
{
	mblk_t *mp;
	int err = -EOPNOTSUPP;

	while ( (mp = getq(q)) )
	{
		if ( mp->b_datap->db_type < QPCTL && !canputnext(q) )
		{
			putbq(q, mp);
			return(0);
		}
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:
				if ( (err = m2pa_r_data(q, mp)) )
					break;
				goto rsrv_continue;
			case M_PROTO:
				if ( (err = m2pa_r_proto(q, mp)) )
					break;
				goto rsrv_continue;
			case M_PCPROTO:
				if ( (err = m2pa_r_pcproto(q, mp)) )
					break;
				goto rsrv_continue;
			case M_CTL:
				if ( (err = m2pa_r_ctl(q, mp)) )
					break;
				goto rsrv_continue;
		}
		switch ( err )
		{
			case -EAGAIN:
				if ( mp->b_datap->db_type < QPCTL )
				{
					putbq(q, mp);
					return(0);
				}
			case -EOPNOTSUPP:
				if ( q->q_next )
				{
					putnext(q, mp);
					goto rsrv_continue;
				}
				break;
		}
		freemsg(mp);
rsrv_continue:
		/*
		 *  NOTE:-  I have removed and processed a message from the
		 *  receive queue, so I should check for receive congestion
		 *  abatement.  If receive congestion has abated below the
		 *  accept threshold, I should signal MTP that there is no
		 *  longer any receive congestion.
		 */
		continue;
	}
	return(0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  WRITE QUEUE PUT and SRV routines
 *
 *  -------------------------------------------------------------------------
 *
 *  M2PA WPUT - Message from above.
 *
 *  If the message is priority message we attempt to process it immediately.
 *  If the message is non-priority message, but there are no messages on the
 *  queue yet, we attempt to process it immediately.  If the message is not
 *  supported, we pass it down-queue if possible.  If the message cannot be
 *  processed immediately, we place it on the queue.
 */
static INT
m2pa_wput(queue_t *q, mblk_t *mp)
{
	mblk_t *mp;
	int err = -EOPNOTSUPP;
	if ( q->q_count && mp->b_datap->db_type < QPCTL )
	{
		putq(q, mp);
		/*
		 *  NOTE:- after placing messages on the queue here, I should
		 *  check for transmit congestion.  I should check if placing
		 *  the message on the queue crosses a band threshold for
		 *  congestion onset and abatement.  When crossing congestion
		 *  thresholds, I should notify MTP3.
		 */
		return(0);
	}
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:
			if ( (err = m2pa_w_data(q, mp)) )
				break;
			return(0);
		case M_PROTO:
			if ( (err = m2pa_w_proto(q, mp)) )
				break;
			return(0);
		case M_PCPROTO:
			if ( (err = m2pa_w_pcproto(q, mp)) )
				break;
			return(0);
		case M_CTL:
			if ( (err = m2pa_w_ctl(q, mp)) )
				break;
			return(0);
		case M_IOCTL:
			if ( (err = m2pa_w_ioctl(q, mp)) )
				break;
			return(0);
		case M_FLUSH:
			m2pa_w_flush(q, mp);
			return(0);
	}
	switch ( err )
	{
		case -EAGAIN:
			if ( mp->b_datap->db_type < QPCTL )
			{
				putq(q, mp);
				return(0);
			}
		case -EOPNOTSUPP:
			if ( q->q_next )
			{
				putnext(q, mp);
				return(0);
			}
	}
	freemsg(mp);
	return(err);
}
/*
 *  M2PA WSRV = Queued message from above.
 *
 *  If the message is a priority message we attempt to process it immediately
 *  and without flow control.  If the message is a non-priority message and
 *  the next queue is flow controlled, we put the message back on the queue
 *  and wait.  If we cannot process a priority message immediately we cannot
 *  place it back on the queue, we discard it.  We requeue non-priority
 *  messages which cannot be processed immediately.  Unrecognized messages are
 *  passed down-queue.
 */
static INT
m2pa_wsrv(queue_t *q)
{
	int err = -EOPNOTSUPP;
	mblk_t *mp;

	while ( (mp = getq(q)) )
	{
		if ( q->q_next && mp->b_datap->db_type < QPCTL && !canputnext(q))
		{
			putbq(q, mp);
			return(0);
		}
		switch ( mp->b_datap->db_type )
		{
			case M_DATA:
				if ( (err = m2pa_w_data(q, mp)) )
					break;
				goto wsrv_continue;
			case M_PROTO:
				if ( (err = m2pa_w_proto(q, mp)) )
					break;
				goto wsrv_continue;
			case M_PCPROTO:
				if ( (err = m2pa_w_pcproto(q, mp)) )
					break;
				goto wsrv_continue;
			case M_CTL:
				if ( (err = m2pa_w_ctl(q, mp)) )
					break;
				goto wsrv_continue;
		}
		switch ( err )
		{
			case -EAGAIN:
				if ( mp->b_datap->db_type < QPCTL )
				{
					putbq(q, mp);
					return(0);
				}
			case -EOPNOTSUPP:
				if ( q->q_next )
				{
					putnext(q, mp);
					goto wsrv_continue;
				}
		}
		freemsg(mp);
wsrv_continue:
		/*
		 *  NOTE:-   I have removed a message from the queue, so I
		 *  should check for band congestion abatement for the data
		 *  band to see if transmit congestion has abated.  If it has,
		 *  I should notify MTP3 of transmit abatement.
		 */
		continue;
	}
	return(0);
}


/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
/*
 *  Private structure allocation and deallocation.
 *
 *  We use Linux hardware aligned cache here for speedy access to information
 *  contained in the private data structure.
 */
kmem_cache_t *m2pa_cachep = NULL;

static m2pa_t *
m2pa_alloc_priv(queue_t *q)
{
	m2pa_t *m2pa;

	if ( (m2pa = kmem_cache_alloc(m2pa_cachep)) )
	{
		bzero(m2pa, sizeof(*m2pa));
		RD(q)->q_ptr = WR(q)->q_tpr = m2pa;
		m2pa->rq = RD(q);
		m2pa->wq = WR(q);
	}
	return(m2pa);
}

static void
m2pa_free_priv(queue_t *q)
{
	m2pa_t *m2 = (m2pa_t *)q->q_ptr;
	kmem_cache_free(m2pa_cachep, m2pa);
	return;
}

static void
m2pa_init_priv(void)
{
	if ( !(m2pa_cachep = kmem_find_general_cachep(sizeof(m2pa_t))) )
	{
		/* allocate a new cachep */
	}
	return;
}


/*
 *  -------------------------------------------------------------------------
 *
 *  OPEN - Each open
 *
 *  -------------------------------------------------------------------------
 */
static int
m2pa_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
	(void)crp;

	if ( q->q_ptr != NULL )
		return(0);

	/*
	 *  FIXME: we have to open this as a driver, not a module
	 */
	if ( sflag == MODOPEN || WR(q)->q_next != NULL )
	{
		/*
		 *  FIXME: check to make sure that the module we are being
		 *  pushed over is compatible (i.e. it is the right kind of
		 *  transport module.
		 */
		if  ( !(m2pa = kmem_cache_alloc(m2pa_m2pa_cachep,
						SLAB_HWCACHE_ALIGN)) )
			return ENOMEM;
		RD(q)->q_ptr = WR(q)->q_ptr = m2pa;
		m2pa->rq = RD(q);
		m2pa->wq = WR(q);
		/*
		 *  TODO: more structure initialization
		 */
		return(0);
	}
	return EIO;
}

/*
 *  -------------------------------------------------------------------------
 *
 *  CLOSE - Last close
 *
 *  -------------------------------------------------------------------------
 */
static int
m2pa_close(queue_t *q, int flag, cred_t *crp)
{
	m2pa_t *m2pa = (m2pa_t *)q->q_ptr;

	(void)flag;
	(void)crp;

	/*
	 *  TODO: make sure everything is deallocated
	 */
	kmem_cache_free(m2pa_m2pa_cachep, m2pa);
	return(0);
}

/*
 *  =========================================================================
 *
 *  LiS MODULE INITIALIZATION
 *
 *  =========================================================================
 */

static int m2pa_initialized = 0;

#ifndef LIS_REGISTERED
static inline void m2pa_init(void)
#else
__initfunc(void m2pa_init(void))
#endif
{
	if ( m2pa_initialized )
		return;
	m2pa_initialized = 1;
	cmn_err(CE_NOTE, M2PA_BANNER);	/* console splash */
#ifndef LIS_REGISTERED
	if ( !(m2pa_minfo.mi_idnum = lis_register_strmod(&m2pa_info, m2pa_minfo.mi_idname)) )
	{
		cmn_err(CE_NOTE, "m2pa: couldn't register as module\n");
		m2pa_minfo.mi_idnum = 0;
	}
#endif
}

#ifndef LIS_REGISTERED
static inline void m2pa_terminate(void)
#else
__initfunc(void m2pa_terminate(void))
#endif
{
	if ( !m2pa_initialized )
		return;
	m2pa_initialized = 0;
#ifndef LIS_REGISTERED
	if ( m2pa_minfo.mi_idnum )
		if ( (sdt_minfo.mi_idnum = lis_unregister_strmod(&m2pa_info)) )
		{
			cmn_err(CE_WARN, "sdt: couldn't unregister as module!\n");
		}
#endif
};

/*
 *  =========================================================================
 *
 *  LINUX KERNEL MODULE INITIALIZATION
 *
 *  =========================================================================
 */

#ifdef MODULE
int init_module(void)
{
	m2pa_init();
	return(0);
}
void cleanup_module(void)
{
	m2pa_terminate();
	return;
}
#endif
