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

 @(#) m2pa.c,v 0.7.8.1 2001/12/11 13:15:13 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:13 by brian

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

 m2pa.c,v
 Revision 0.7.8.1  2001/12/11 13:15:13  brian
 Branched for new development.

 Revision 0.7.6.5  2001/12/07 08:37:28  brian
 Passes large quantities of test cases.

 Revision 0.7.6.4  2001/12/01 21:51:18  brian
 Going to shift to style1.

 Revision 0.7.6.3  2001/06/03 18:31:59  brian
 Made a few changes.

 Revision 0.7.6.2  2001/05/24 06:32:15  brian
 More work on M2PA.

 Revision 0.7.6.1  2001/05/23 09:53:00  brian
 Working up M2PA some more.

 Revision 0.7  2001/05/23 07:37:15  brian
 Working on M2PA per draft-ietf-sigtran-m2pa-02.txt

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

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

#define M2PA_DESCRIP	"M2PA/SCTP STREAMS MODULE."
#define M2PA_COPYRIGHT	"Copyright (c) 2001 OpenSS7 Corp.  All Rights Reserved."
#define M2PA_DEVICES	"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_DEVICES	"\n" \
			M2PA_CONTACT	"\n"

#ifdef MODULE
MODULE_AUTHOR(M2PA_CONTACT);
MODULE_DESCRIPTION(M2PA_DESCRIP);
MODULE_SUPPORTED_DEVICE(M2PA_DEVICE);
#define MODULE_STATIC static
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#define MODULE_STATIC
#endif

#ifdef M2PA_DEBUG
static int m2pa_debug = M2PA_DEBUG;
#else
static int m2pa_debug = 2;
#endif

#ifndef M2PA_CMAJOR
#define M2PA_CMAJOR 240
#endif
#define M2PA_NMINOR 255

#ifdef LIS_2_12
#define INT int
#else
#define INT void
#endif

typedef void (*bufcall_fnc_t)(long);

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */

static struct module_info m2pa_info =
{
	0,	/* Module ID number		*/	/* FIXME */
	"m2pa",	/* Module name			*/
	1,	/* Min packet size accepted	*/
	512,	/* Max packet size accepted	*/
	8*512,	/* Hi water mark		*/
	1*512	/* Lo water mark		*/
};

static INT m2pa_rput (queue_t *, mblk_t *);
static INT m2pa_rsrv (queue_t *);
static int m2pa_open (queue_t *, dev_t *, int, int, cred_t *);
static int m2pa_close(queue_t *, int, cred_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			*/
};

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

#define M2PA_PRIV(__q) ((m2pa_t *)(__q)->q_ptr)

typedef struct m2pa {
	struct m2pa *next;
	queue_t	    *rq;	/* STREAM read  queue		*/
	queue_t	    *wq;	/* STREAM write queue		*/
	uint	    state;	/* M2PA state			*/
	uint	    t_state;	/* T-Provider state		*/
	sl_state_t  sl_state;	/* Signalling Link State	*/
} m2pa_t;

#define M2PA_IDLE		0
#define M2PA_OUT_OF_SERVICE	1
#define M2PA_NOT_ALIGNED	2
#define M2PA_ALIGNED		3
#define M2PA_ALIGNED_NOT_READY	4
#define M2PA_PROVING		5
#define M2PA_PROVING_FAILED	6
#define M2PA_ALIGNED_READY	7
#define M2PA_IN_SERVICE		8

#define M2PA_LOC_EMERGENCY	0x0001
#define M2PA_REM_EMERGENCY	0x0002
#define M2PA_EMERGENCY		(M2PA_LOC_EMERGENCY|M2PA_REM_EMERGENCY)
#define M2PA_LOC_PROC_OUTAGE	0x0004
#define M2PA_REM_PROC_OUTAGE	0x0008
#define M2PA_PROC_OUTAGE	(M2PA_LOC_PROC_OUTAGE|M2PA_REM_PROC_OUTAGE)

#define M2PA_STATUS_OUT_OF_SERVICE	(__constant_htonl(0))
#define M2PA_STATUS_IN_SERVICE		(__constant_htonl(1))
#define	M2PA_STATUS_PROCESSOR_OUTAGE	(__constant_htonl(2))
#define M2PA_STATUS_PROCESSOR_ENDED	(__constant_htonl(3))
#define M2PA_STATUS_BUSY		(__constant_htonl(4))
#define M2PA_STATUS_BUSY_ENDED		(__constant_htonl(5))
#define M2PA_STATUS_ALIGN		(__constant_htonl(6))
#define M2PA_STATUS_NORMAL_PROVING	(__constant_htonl(7))
#define M2PA_STATUS_EMERGENCY_PROVING	(__constant_htonl(8))

#define M2PA_STATUS_SIO		0
#define M2PA_STATUS_SIN		1
#define M2PA_STATUS_SIE		2
#define M2PA_STATUS_SIOS	3
#define M2PA_STATUS_SIPO	4
#define M2PA_STATUS_SIB		5
#define M2PA_STATUS_SIIS	6


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

/*
 *  LMI_INFO_ACK
 *
 *  Builds an LMI_INFO_ACK primiive given the current state of the Signalling
 *  Link interface and the Physical Point of Appearance address to which the
 *  interface is attached.
 *
 */
static inline mblk_t *lmi_info_ack(uint state, caddr_t ppa_ptr, size_t ppa_len)
{
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ( (mp = allocb(sizeof(*p)+ppa_len, 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	    = state;
		p->lmi_max_sdu	    = -1;
		p->lmi_min_sdu	    = 1;
		p->lmi_header_len   = 0;
		p->lmi_ppa_style    = LMI_STYLE2;
		mp->b_wptr += sizeof(*p);
		bcopy(ppa_ptr, mp->b_wptr, ppa_len);
		mp->b_wptr += ppa_len;
	}
	return(mp);
}

/*
 *  LMI_OK_ACK
 *
 *  Builds an LMI_OK_ACK primitive given the correct primitive and the state
 *  of the interface.
 */
static inline mblk_t *lmi_ok_ack(int prim, uint state)
{
	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;
		p->lmi_state		= state;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  LMI_ERROR_ACK
 *
 *  Builds an LMI_ERROR_ACK primitive given the errored primitive, the error,
 *  reason and the state of the interface.
 */
static inline mblk_t *lmi_error_ack(int prim, int err, int reason, uint state)
{
	mblk_t *mp;
	lmi_error_ack_t *p;
	if ( (mp = allocb(sizeof(*p), BRPI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (lmi_error_ack_t *)mp->b_wptr;
		p->lmi_primitive	= LMI_ERROR_ACK;
		p->lmi_error_primitive	= prim;
		p->lmi_errno		= err;
		p->lmi_reason		= reason;
		p->lmi_state		= state;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  LMI_ENABLE_CON
 *
 *  Builds and LMI_ENABLE_CON primitive.
 */
static inline mblk_t *lmi_enable_con(void)
{
	mblk_t *mp;
	lmi_enable_con_t *p;
	if ( (mp = allocb(sizeof(*p), BRPI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (lmi_enable_con_t *)mp->b_wptr;
		p->lmi_primitive	= LMI_ENABLE_CON;
		p->lmi_state		= LMI_ENABLED;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  LMI_DISABLE_CON
 *
 *  Builds an LMI_DISABLED_CON primitive.
 */
static inline mblk_t *lmi_disable_con(void)
{
	mblk_t *mp;
	lmi_disable_con_t *p;
	if ( (mp = allocb(sizeof(*p), BRPI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (lmi_disable_con_t *)mp->b_wptr;
		p->lmi_primitive	= LMI_ENABLE_CON;
		p->lmi_state		= LMI_DISABLED;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  LMI_ERROR_IND
 *
 *  Builds an LMI_ERROR_IND primitive given the error, reason and state of the
 *  interface.
 */
static inline mblk_t *lmi_error_ind(int err, int reason, uint state)
{
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BRPI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (lmi_error_ind_t *)mp->b_wptr;
		p->lmi_primitive    = LMI_ERROR_ACK;
		p->lmi_errno	    = err;
		p->lmi_reason	    = reason;
		p->lmi_state	    = state;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  LMI_STATS_IND
 *
 *  Builds an LMI_STATS_IND primitive given the state interval.  (This
 *  primitive will probably be reworked.)
 *
 */
static inline mblk_t *lmi_stats_ind(uint interval)
{
	mblk_t *mp;
	lmi_stats_ind_t *p;
	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	= jiffies;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  LMI_EVENT_IND
 *
 *  Builds an LMI_EVENT_IND primitive given the event object id and the
 *  severity.  (This primitive will probably be reworked.)
 */
static inline mblk_t *lmi_event_ind(uint objectid, uint severity)
{
	mblk_t *mp;
	lmi_event_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_MED)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (lmi_event_ind_t *)mp->b_wptr;
		p->lmi_primitive	= LMI_EVENT_IND;
		p->lmi_objectid		= objectid;
		p->lmi_timestamp	= jiffies;
		p->lmi_severity		= severity;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

/*
 *  SL_PDU_IND
 *
 *  Builds a protocol data unit indication given the data which is contained
 *  in the PDU.
 */
static inline mblk_t *sl_pdu_ind(mblk_t *dp)
{
	mblk_t *mp;
	sl_pdu_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_PDU_IND;
		mp->b_wptr += sizeof(*p);
		mp->b_cont = dp;
	}
	return(mp);
}
/*
 *  SL_LINK_CONGESTED_IND
 *
 *  Builds a link congested indication given the congestion status and discard
 *  status.
 */
static inline mblk_t *sl_link_congested_ind(int cong, int disc)
{
	mblk_t *mp;
	sl_link_congested_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p->sl_primitive = SL_LINK_CONGESTED_IND;
		p->sl_timestamp = jiffies;
		p->sl_cong_status = cong;
		p->sl_disc_status = disc;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_LINK_CONGESTION_CEASED_IND
 *
 *  Build a link congestion ceased indication given the congestion status and
 *  the discard status.
 */
static inline mblk_t *sl_link_congestion_ceased_ind(int cong, int disc)
{
	mblk_t *mp;
	sl_link_congestion_ceased_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p->sl_primitive = SL_LINK_CONGESTION_CEASED_IND;
		p->sl_timestamp = jiffies;
		p->sl_cong_status = cong;
		p->sl_disc_status = disc;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_RETREIVED_MESSAGE_IND
 *
 *  Builds a retrieved message indication, given the M_DATA blocks of the
 *  retrieved message.
 */
static inline mblk_t *sl_retreived_message_ind(mblk_t *dp)
{
	mblk_t *mp;
	sl_retrieved_message_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_RETRIEVED_MESSAGE_IND;
		mp->b_wptr += sizeof(*p);
		mp->b_cont = dp;
	}
	return(mp);
}
/*
 *  SL_RETREIVAL_COMPLETE_IND
 *
 *  Builds a retrieval complete indication.
 */
static inline mblk_t *sl_retreival_complete_ind(void)
{
	mblk_t *mp;
	sl_retreival_complete_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_RETREIVAL_COMPLETE_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_RB_CLEARED_IND
 *
 *  Builds a receive buffer cleared indication.
 */
static inline mblk_t *sl_rb_cleared_ind(void)
{
	mblk_t *mp;
	sl_rb_cleared_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_RB_CLEARED_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_BSNT_IND
 *
 *  Builds a BSNT confirmation, returning the BSNT which was requested to be
 *  retrieved.
 */
static inline mblk_t *sl_bsnt_ind(int bsnt)
{
	mblk_t *mp;
	sl_bsnt_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_BSNT_IND;
		p->sl_bsnt = bsnt;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_IN_SERVICE_IND
 *
 *  Builds an in service indication.
 */
static inline mblk_t *sl_in_service_ind(void)
{
	mblk_t *mp;
	sl_in_service_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_IN_SERVICE_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_OUT_OF_SERVICE_IND
 *
 *  Builds an out of service indication given the reason for link failure.
 */
static inline mblk_t *sl_out_of_service_ind(int reason)
{
	mblk_t *mp;
	sl_out_of_service_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p->sl_primitive = SL_OUT_OF_SERVICE_IND;
		p->sl_timestamp = jiffies;
		p->sl_reason = reason;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_REMOTE_PROCESSOR_OUTAGE_IND
 *
 *  Builds a remote processor outage indication.
 */
static inline mblk_t *sl_remote_processor_outage_ind(void)
{
	mblk_t *mp;
	sl_rem_proc_out_ind_t *p;
	if ( (mp = allocb(sizoef(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p->sl_primitive = SL_REMOTE_PROCESSOR_OUTAGE_IND;
		p->sl_timestamp = jiffies;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_REMOTE_PROCESSOR_RECOVERED_IND
 *
 *  Builds a remote processor recovered indication.
 */
static inline mblk_t *sl_remote_processor_recovered_ind(void)
{
	mblk_t *mp;
	sl_rem_proc_recovered_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PCPROTO;
		p->sl_primitive = SL_REMOTE_PROCESSOR_RECOVERED_IND;
		p->sl_timestamp = jiffies;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_RTB_CLEARED_IND
 *
 *  Builds a retransmit buffer cleared indication.
 */
static inline mblk_t *sl_rtb_cleared_ind(void)
{
	mblk_t *mp;
	sl_rtb_cleared_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_RTB_CLEARED_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_RETRIEVAL_NOT_POSSIBLE_IND
 *
 *  bUilds a retrieval not possible indication.
 */
static inline mblk_t *sl_retieval_not_possible_ind(void)
{
	mblk_t *mp;
	sl_retrieval_not_poss_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_RETRIEVAL_NOT_POSSIBLE_IND;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  SL_BSNT_NOT_RETRIEVABLE_IND
 *
 *  Builds a BSNT not retrievable indication given the best value of the BSNT
 *  which could be used. (Why the BSNT?)
 */
static inline mblk_t *sl_bsnt_not_retrievable_ind(int bsnt)
{
	mblk_t *mp;
	sl_bsnt_not_retr_ind_t *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p->sl_primitive = SL_BSNT_NOT_RETRIEVABLE_IND;
		p->bsnt = bsnt;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}

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

/*
 *  T_INFO_REQ
 *
 *  We might use this primitive if the T-Provider is not SCTP to check the
 *  capabilities of the T-Provider before exchanging any other primitives.
 */
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_BIND_REQ
 *
 *  This is used by M2PA to bind the T-Provider to a local transport address
 *  before attempting to connect the T-Provider.  This builds the bind request
 *  primitive given a point and length of the address and the number of
 *  outstanding connection indications which is required.
 */
static inline mblk_t *t_bind_req(caddr_t add_ptr, size_t add_len, int 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	 = sizeof(*p);
		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_OPTMGMT_REQ
 *
 *  This might be used by M2PA to set some local and associative options
 *  before establishing a connection.
 */
static inline mblk_t *t_optmgmt_req(caddr_t opt_ptr, size_t opt_len, int flags)
{
	mblk_t *mp;
	struct T_optmgmt_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_OPTMGMT_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_ADDR_REQ
 *
 *  This is really not necessary, but it is here anyways.  Particularly if
 *  there is a non-SCTP T-Providder which does not provide the address
 *  information in a usable (or storable) format in the appropriate
 *  primitives.  This should be unnecessary for the OpenSS7 SCTP T-Provider.
 */
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;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_CONN_REQ
 *
 *  This is used to actively request a connection to the transport peer.
 *
 *  Builds a transport connection request given the destination address and
 *  the options associated with the connection.
 */
static inline mblk_t *t_conn_req(caddr_t dst_ptr, size_t dst_len,
		      caddr_t opt_ptr, 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_wptr;
		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
 *
 *  This is used to accept a passive connection from the transport peer.  We
 *  use XTI/TLI semantics here an always accept the connection on the same
 *  stream which was listening.  Remember that SCTP can provide a T_CONN_IND
 *  while in the T_DATAXFER state (for SCTP Restart) which must also be
 *  accepted on this stream.
 */
static inline mblk_t *t_conn_res(queue_t *q,
		caddr_t opt_ptr, size_t opt_len, int seq,
		mblk_t *dp)
{
	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	= opt_len?sizeof(*p):0;
		p->SEQ_number	= seq;
		mp->b_wptr += sizeof(*p);
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		mp->b_cont = dp;
	}
	return(mp);
}
/*
 *  T_DATA_REQ
 *
 *  This is used to send normal (ordered) messages on the control or default
 *  SCTP-stream (stream 0).
 *
 */
static inline mblk_t *t_data_req(mblk_t *dp)
{
	mblk_t *mp;
	struct T_data_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		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);
		mp->b_cont = dp;
	}
	return(mp);
}
/*
 *  T_EXDATA_REQ
 *
 *  This is used to send expedited (really, unordered) messages on the control
 *  or default SCTP-stream (stream 0).
 */
static inline mblk_t *t_exdata_req(mblk_t *dp)
{
	mblk_t *mp;
	struct T_exdata_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = 1;
		p = (struct T_exdata_req *)mp->b_wptr;
		p->PRIM_type	= T_EXDATA_REQ;
		p->MORE_flag	= 0;
		mp->b_wptr += sizeof(*p);
		mp->b_cont = dp;
	}
	return(mp);
}
/*
 *  T_OPTDATA_REQ
 *
 *  This is used to send normal (ordered) or expedited (unordered) data on a
 *  specific stream.  This builds a T_OPTDATA_REQ given the data flags,
 *  options pointer and length, and the M_DATA blocks to include in the
 *  message.
 */
static inline mblk_t *t_optdata_req(int flags, caddr_t opt_ptr, size_t opt_len, mblk_t *dp)
{
	mblk_t *mp;
	struct T_optdata_req *p;
	if ( (mp = allocb(sizeof(*p)+opt_len, BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = (flags & M2PA_URGENT) ? 1 : 0;
		p = (struct T_optdata_req *)mp->b_wptr;
		p->PRIM_type	= T_OPTDATA_REQ;
		p->DATA_flag	= flags;
		p->OPT_length	= opt_len;
		p->OPT_offset	= opt_len?sizeof(*p):0;
		mp->b_wptr += sizeof(*p);
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		mp->b_cont = dp;
	}
	return(mp);
}
/*
 *  T_DISCON_REQ
 *
 *  This is used to ABORT the SCTP association under brutal circumstances.
 */
static inline mblk_t *t_discon_req(int seq)
{
	mblk_t *mp;
	struct T_discon_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		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
 *
 *  This is used to peform a SHUTDOWN on the SCTP association.
 */
static inline mblk_t *t_ordrel_req(void)
{
	mblk_t *mp;
	struct T_ordrel_req *p;
	if ( (mp = allocb(sizeof(*p), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_ordrel_req *)mp->b_wptr;
		p->PRIM_type	= T_DISCON_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return(mp);
}
/*
 *  T_UNITDATA_REQ
 *
 *  This is not normally used for a SCTP T-Provider (which is *  connection-
 *  oriented only).  However, if we use a connectionless T-Provider we may use
 *  these messages, so here it is.
 */
static inline mblk_t *t_unitdata_req(
		caddr_t dst_ptr, size_t dst_len,
		caddr_t opt_ptr, size_t opt_len, mblk_t *dp)
{
	mblk_t *mp;
	struct T_unitdata_req *p;
	if ( (mp = allocb(sizeof(*p)+dst_len+opt_len, BPRI_HI)) )
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_unitdata_req *)mp->b_wptr;
		p->PRIM_type	= T_DISCON_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);
}
}

/*
 *  Mapping of states:
 *
 *  M2PA State		    T-Provider State(s)
 *  -------------------	    --------------------------------------------
 *  M2PA_STATE_IDLE	    (uninitialized)
 *
 *  M2PA_STATE_OOS	    TS_UNBND TS_WACK_UREQ TS_IDLE TS_WACK_OPREQ
 *			    TS_WACK_DREQ6 TS_WACK_DREQ7 TS_WACK_DREQ9
 *			    TS_WACK_DREQ10 TS_WACK_DREQ11 TS_WIND_ORDREL
 *			    TS_WACK_CREQ TS_WACK_CRES
 *
 *  M2PA_STATE_AIP	    TS_WACK_BREQ TS_IDLE TS_WCON_CREQ TS_WRES_CIND
 *			    TS_WACK_OPREQ TS_WACK_CREQ TS_WACK_CRES
 *
 *  M2PA_STATE_INS_LOCAL    TS_DATA_XFER
 *
 *  M2PA_STATE_INS	    TS_DATA_XFER TS_WREQ_ORDREL
 *
 *  M2PA_RETRIEVAL	    TS_WIND_ORDREL TS_WACK_DREQ6 TS_WACK_DREQ7
 *			    TS_WACK_DREQ9 TS_WACK_DREQ10 TS_WACK_DREQ11
 *			    TS_WACK_CREQ TS_WACK_CRES TS_IDLE TS_WACK_UREQ
 *			    TS_UNBND
 *  -------------------	    --------------------------------------------
 */

/*
 *  =========================================================================
 *
 *  M2PA-Provider --> M2PA-Peer Primitives (Sent Messages)
 *
 *  =========================================================================
 */
/*
 *  M2PA SEND DATA
 *
 *  TODO:- We should probably be doing several more things here.  We should be
 *  invoking an option to have data acknowledged to us when it is acknowledged
 *  by the other end and have the SSN and TSN returned to us.  Also we should
 *  place a token on the data to permit correlating acknowledgements with the
 *  outgoing data (use the mblk_t pointer).  Also, we might want to dig into
 *  the M_DATA blocks and pull out the SLS for the message so that we can
 *  choose the stream id to send on based on the SLS.
 */
static inline mblk_t *m2pa_send_data(mblk_t *dp)
{
	mblk_t *mp;
	struct T_optdata_req *p;
	static struct sctp_opt_sid opt = {
		{ T_INET_SCTP, T_SCTP_SID, sizeof(opt) }, 1 } };
	
	return t_optdata_req( 0, (caddr_t)&opt, sizeof(opt), dp );
}
/*
 *  M2PA SEND STATUS
 *
 *  I have added sending M2PA_STATUS_OUT_OF_SERVICE here because I believe
 *  that there is no reason for dropping the SCTP association is there is no
 *  need to do so.
 */
static inline mblk_t *m2pa_send_status(uint32_t status)
{
	mblk_t *mp;
	if ( (mp = allocb(sizeof(uint32_t), BPRI_HI)) )
	{
		mp->b_datap->db_type = M_DATA;
		mp->b_band = M2PA_HI_PRIORITY;
		*((uint32_t *)mp->b_wptr)++ = status;
	}
	return(mp);
}
static inline mblk_t *m2pa_send_in_service(void) {
	return m2pa_send_status(M2PA_STATUS_IN_SERVICE);
}
static inline mblk_t *m2pa_send_sipo(void) {
	return m2pa_send_status(M2PA_STATUS_PROCESSOR_OUTAGE);
}
static inline mblk_t *m2pa_send_not_sipo(void) {
	return m2pa_send_status(M2PA_STATUS_PROCESSOR_ENDED);
}
static inline mblk_t *m2pa_send_sib(void) {
	return m2pa_send_status(M2PA_STATUS_STATUS_BUSY);
}
static inline mblk_t *m2pa_send_not_sib(void) {
	return m2pa_send_status(M2PA_STATUS_STATUS_BUSY_ENDED);
}
static inline mblk_t *m2pa_send_sios(void) {
	return m2pa_send_status(M2PA_STATUS_OUT_OF_SERVICE);
}
static inline mblk_t *m2pa_send_sio(void) {
	return m2pa_send_status(M2PA_STATUS_ALIGN);
}
static inline mblk_t *m2pa_send_sin(void) {
	return m2pa_send_status(M2PA_NORMAL_PROVING);
}
static inline mblk_t *m2pa_send_sie(void) {
	return m2pa_send_status(M2PA_EMERGENCY_PROVING);
}

/*
 *  =========================================================================
 *
 *  M2PA-Peer --> M2PA-Provider Primitives (Received Messages)
 *
 *  =========================================================================
 */
static inline int m2pa_recv_in_service(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);

	switch ( m2->state )
	{
	case M2PA_AIP:
		if ( m2->emergency )
		{
			/*
			 *  IMPLEMENTATION NOTE:-  Here is one case where we
			 *  can speed things up.  If the other end thiks that
			 *  the link is fine (because it has had a chance to
			 *  do a SRTT calculation and measurement) it must be
			 *  good for us too.  Just move to the in service
			 *  state.
			 */
			if ( !(mp = sl_in_service_ind()) )
				return -EAGAIN;
			m2->state = M2PA_INS;
			freemsg(tp);
			putnext(q, mp);
			return(0);
		}
		/*
		 *  Wait for the local alignment procedure to completed
		 *  normally.  Remember that the peer M2PA is in service.
		 */
		m2->remote_in_service = 1;
		return(0);

	case M2PA_INS_LOCAL:
		if ( !(mp = sl_in_service_ind()) )
			return -EAGAIN;
		m2->state = M2PA_INS;
		freemsg(tp);
		putnext(q, mp);
		return(0);

	default:
	case M2PA_OOS:
	case M2PA_IDLE:
	case M2PA_RETRIEVAL:
		/* we shouldn't be getting these, ignore */
		return(0);
	}
	return(0);
}
static inline int m2pa_recv_sipo(queue_t *q, mblk_t *pdu)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	if ( !(m2->flags & M2PA_FLAG_REM_PROC_OUT) )
	{
		if ( sl_processor_outage_ind(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->flags |= M2PA_FLAG_REM_PROC_OUT;
	}
	return(0);
}
static inline int m2pa_recv_not_sipo(queue_t *q, mblk_t *pdu)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	if ( m2->flags & M2PA_FLAG_REM_PROC_OUT )
	{
		if ( sl_processor_recovered_ind(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->flags &= ~M2PA_FLAG_REM_PROC_OUT;
	}
	return(0);
}
static inline int m2pa_recv_sib(queue_t *q, mblk_t *pdu)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	if ( !(m2->flags & M2PA_FLAG_REM_CONG) )
	{
		if ( sl_XXXX_ind(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->flags |= M2PA_FLAG_REM_CONG;
		m2->t6_timer = timeout(m2->t6_timeout, (long)m2, m2->t6_value);
	}
	return(0);
}
static inline int m2pa_recv_not_sib(queue_t *q, mblk_t *pdu)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	if ( m2->flags & M2PA_FLAG_REM_CONG )
	{
		if ( sl_XXXX_ind(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->flags &= ~M2PA_FLAG_REM_CONG;
		untimeout(m2->t6_timer); m2->t6_timer = 0;
	}
	return(0);
}
static inline int m2pa_recv_sios(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	switch ( m2->state )
	{
		case M2PA_ALIGNED:
		case M2PA_PROVING:
			if ( !(mp = sl_out_of_service_ind(SL_FAIL_ALIGNMENT_NOT_POSSIBLE)) )
				return -EAGAIN;
			untimeout(m2->t4_timer); m2->t4_timer = 0;
			m2->t4_timer = 0;
			/*
			 *  Stop AERM functions.
			 */
			untimeout(m2->t3_timer); m2->t3_timer = 0;
			m2->t3_timer = 0;
			m2->flags &= ~M2PA_EMERGENCY;
			m2->state = M2PA_OUT_OF_SERVICE;
			putnext(q, mp);
			break;
	}
	freemsg(pdu);
	return(0);
}
static inline int m2pa_recv_sio(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	switch ( m2->state )
	{
		case M2PA_NOT_ALIGNED:
			if ( m2->flags & M2PA_LOC_EMERGENCY ) {
				if ( !(mp = m2pa_send_sie()) )
					return -EAGAIN;
			} else {
				if ( !(mp = m2pa_send_sin()) )
					return -EAGAIN;
			}
			untimeout(m2->t2_timer); m2->t2_timer = 0;
			m2->t3_timer = timeout(m2->t3_timeout, (long)m2, m2->t3_value);
			m2->state = M2PA_ALIGNED;
			freemsg(pdu);
			qreply(q, mp);
			return(0);

		case M2PA_PROVING:
			untimeout(m2->t4_timer); m2->t4_timer = 0;
			/*
			 *  FIXME: stop AERM function
			 */
			break;
			m2->t3_timer = timeout(m2->t3_timeout, (long)m2, m2->t3_value);
			m2->state = M2PA_ALIGNED;
			freemsg(pdu);
			return(0);

	}
}
static inline int m2pa_recv_sin(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	switch ( m2->state )
	{
		case M2PA_NOT_ALIGNED:
			untimeout(m2->t2_timer); m2->t2_timer = 0;
			m2->t2_timer = 0;
			if ( m2->flags & M2PA_LOC_EMERGENCY ) {
				if ( !(mp = m2pa_send_sie()) )
					return -EAGAIN;
			} else {
				if ( !(mp = m2pa_send_sin()) )
					return -EAGAIN;
			}
			m2->t3_timer = timeout(m2->t3_timeout, (long)m2, m2->t3_value);
			m2->state = M2PA_ALIGNED;
			qreply(q, mp);
			break;

		case M2PA_ALIGNED:
			untimeout(m2->t3_timer); m2->t3_timer = 0;
			/*
			 *  Start AERM functions
			 */
			m2->t4_timer = timeout(m2->t4_timeout, (long)m2, m2->aerm_opts.t4_value);
			m2->statem.Cp = 0;
			m2->statem.further_proving = 0;
			m2->state = M2PA_PROVING;
	}
	freemsg(pdu);
	return(0);
}
static inline int m2pa_recv_sie(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	switch ( m2->state )
	{
		case M2PA_NOT_ALIGNED:
			if ( m2->flags & M2PA_LOC_EMERGENCY ) {
				if ( !(mp = m2pa_send_sie()) )
					return = -EAGAIN;
			} else {
				if ( !(mp = m2pa_send_sin()) )
					return = -EAGAIN;
			}
			m2->flags |= M2PA_REM_EMERGENCY;
			m2->aerm_opts = m2->aerm_emer_opts;
			untimeout(m2->t2_timer); m2->t2_timer = 0;
			m2->t3_timer = timeout(m2->t3_timeout, (long)m2, m2->t3_value);
			m2->state = M2PA_ALIGNED;
			qreply(q, mp);
			break;

		case M2PA_ALIGNED:
			m2->flags |= M2PA_REM_EMERGENCY;
			m2->aerm_opts = m2->aerm_emer_opts;
			untimeout(m2->t3_timer); m2->t3_timer = 0;
			/*
			 *  Start AERM functions
			 */
			m2->t4_timer = timeout(m2->t4_timeout, (long)m2, m2->aerm_opts.t4_value);
			m2->statem.Cp = 0;
			m2->statem.further_proving = 0;
			m2->state = M2PA_PROVING;
			break;

		case M2PA_PROVING:
			if ( m2->flags & M2PA_EMERGENCY )
				break;
			m2->flags |= M2PA_REM_EMERGENCY;
			m2->aerm_opts = m2->aerm_emer_opts;
			untimeout(m2->t4_timer); m2->t4_timer = 0;
			/*
			 *  Restart AERM functions with new values
			 */
			m2->statem.further_proving = 0;
			m2->t4_timer = timeout(m2->t4_timeout, (long)m2, m2->aerm_opts.t4_value);
			break;
	}
	freemsg(pdu);
	return(0);
}
static inline int m2pa_recv_status(queue_t *q, mblk_t *mp)
{
	int err;
	uint32_t *s = mp->b_rptr;
	static int (*jump[])(queue_t *, mblk_t *) =
	{
		&m2pa_recv_out_of_service,
		&m2pa_recv_in_service,
		&m2pa_recv_processor_outage,
		&m2pa_recv_processor_ended,
		&m2pa_recv_,
		&m2pa_recv_,
		&m2pa_recv_,
		&m2pa_recv_,
		&m2pa_recv_,
		&m2pa_recv_,
	};

	switch ( *s )
	{
	case M2PA_STATUS_IN_SERVICE:	    return m2pa_recv_in_service(q, mp);
	case M2PA_STATUS_PROCESSOR_OUTAGE:  return m2pa_recv_sipo(q, mp);
	case M2PA_STATUS_PROCESSOR_ENDED:   return m2pa_recv_not_sipo(q, mp);
	case M2PA_STATUS_BUSY:		    return m2pa_recv_sib(q, mp);
	case M2PA_STATUS_BUSY_ENDED:	    return m2pa_recv_not_sib(q, mp);
	case M2PA_STATUS_OUT_OF_SERVICE:    return m2pa_recv_sios(q, mp);
	case M2PA_STATUS_ALIGN:		    return m2pa_recv_sio(q, mp);
	case M2PA_STATUS_NORMAL_PROVING:    return m2pa_recv_sin(q, mp);
	case M2PA_STATUS_EMERGENCY_PROVING: return m2pa_recv_sie(q, mp);
	}
	return -EPROTO;
}
static inline int m2pa_recv_data(m2pa_t *m2, mblk_t *dp, uint16_t sid)
{
	switch ( sid )
	{
		case M2PA_STATUS_STREAM:
			return m2pa_recv_status(m2, dp);
		default:
			/*
			 *  IMPLEMENTATION NOTE:- Now here's a concept: why
			 *  not have multiple links over one M2PA SCTP
			 *  association?  Gee they are either all busy or all
			 *  processor outage or all in service so the one
			 *  status channel should  be sufficient.  Multiple
			 *  links in a link-set are just for traffic capacity
			 *  and sizing anyways.
			 */
		case M2PA_DATA_STREAM:
			putnext(m2->rq, mp);
			return(0);
	}
	return -EPROTO;
}

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

/*
 *  -------------------------------------------------------------------------
 *
 *  SL-User --> M2PA-Provider MANAGEMENT Primitives
 *
 *  -------------------------------------------------------------------------
 */

static int lmi_info_req    (queue_t *, mblk_t *);
static int lmi_attach_req  (queue_t *, mblk_t *);
static int lmi_detach_req  (queue_t *, mblk_t *);
static int lmi_enable_req  (queue_t *, mblk_t *);
static int lmi_disable_req (queue_t *, mblk_t *);
static int lmi_optmgmt_req (queue_t *, mblk_t *);

static int (*lmi_m_proto_ops[])(queue_t *, mblk_t *) =
{
	&lmi_info_req,
	&lmi_attach_req,
	&lmi_detach_req,
	&lmi_enable_req,
	&lmi_disable_req,
	&lmi_optmgmt_req
};

/*
 *  LMI_INFO_REQ:
 *  -------------------------------------------------------------------------
 *  We have to get some info from the T-Provider before we respond to this,
 *  so pass the information request on.
 */
static int lmi_info_req(queue_t *q, mblk_t *mp)
{
	mblk_t *resp;
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_info_req_t *p = (lmi_info_req_t *)mp->b_rptr;

	if ( !(resp = t_addr_req()) )
		return -EAGAIN;

	freemsg(mp);
	putnext(q, resp);
	return(0);
}
/*
 *  LMI_ATTACH_REQ:
 *  -------------------------------------------------------------------------
 *  Note that the PPA is only a local thing.  This is the local bind address
 *  for the SCTP association.  If we are not a client, we listen on the
 *  connection.  It takes an LMI_ENABLE command (from a signalling data link
 *  connection order or from management at setup) to provide the address for
 *  connection.
 */
static int lmi_attach_req(queue_t *q, mblk_t *mp)
{
	mblk_t *resp;
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_attach_req_t *p = (lmi_attach_req_t *)mp->b_rptr;

	const caddr_t add_ptr = p->lmi_ppa;
	const size_t  add_len = mp->b_wptr - add_ptr;
	const size_t cons = m2->flags & M2PA_CLIENT ? 0 : 1;

	if ( m2->m_state != LMI_UNATTACHED || m2->t_state != TS_UNBND )
	{
		if ( !(resp = lmi_error_ack(LMI_OUTSTATE, 0, m2->m_state)) )
			return -EAGAIN;
		freemsg(mp);
		qreply(q, resp);
		return(0);
	}
	add_ptr = p->lmi_ppa;
	add_len = mp->b_wptr - add_ptr;

	if ( !(resp = t_bind_req(add_ptr, add_len, cons)) )
		return -EAGAIN;

	m2->t_state = TS_WACK_BREQ;
	m2->m_state = LMI_ATTACH_PENDING;

	freemsg(mp);
	putnext(q, resp);
	return(0);
}
/*
 *  LMI_DETACH_REQ:
 *  -------------------------------------------------------------------------
 */
static int lmi_detach_req(queue_t *q, mblk_t *mp)
{
	mblk_t *resp;
	m2pa_t *m2 = M2PA_PRIV(q);

	lmi_detach_req_t *p = (lmi_detach_req_t *)mp->b_rptr;

	if ( m2->m_state != LMI_UNATTACHED || m2->t_state != TS_IDLE )
	{
		if ( !(resp = lmi_error_ack(LMI_OUTSTATE, 0, m2->m_state)) )
			return -EAGAIN;
		freemsg(mp);
		qreply(q, resp);
		return(0);
	}
	if ( !(resp = t_unbind_req()) )
		return -EAGAIN;

	m2->t_state = TS_WACK_UREQ;

	freemsg(mp);
	putnext(q, resp);
	return(0);
}
/*
 *  LMI_ENABLE_REQ:
 *  -------------------------------------------------------------------------
 */
static int lmi_enable_req(queue_t *q, mblk_t *mp)
{
	mblk_t *resp;
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_enable_req_t *p = (lmi_enable_req_t *)mp->b_rptr;

	const caddr_t add_ptr = (caddr_t)(p+1);
	const size_t  add_len = mp->b_wptr - add_ptr;

	if ( m2->m_state != LMI_DISABLED || m2->t_state != TS_IDLE )
	{
		if ( !(resp = lmi_error_ack(LMI_OUTSTATE, 0, m2->m_state)) )
			return -EAGAIN;
		freemsg(mp);
		qreply(q, resp);
		return(0);
	}
	if ( !(resp = t_conn_req(add_ptr, add_len)) )
		return -EAGAIN;

	m2->t_state = TS_WACK_CREQ;
	m2->m_state = LMI_ENABLE_PENDING;

	freemsg(mp);
	putnext(q, resp);
	return(0);
}
/*
 *  LMI_DISABLE_REQ:
 *  -------------------------------------------------------------------------
 */
static int lmi_disable_req(queue_t *q, mblk_t *mp)
{
	mblk_t *resp;
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_disable_req_t *p = (lmi_disable_req_t *)mp->b_rptr;

	if ( m2->m_state != LMI_ENABLED )
	{
		if ( !(resp = lmi_error_ack(LMI_OUTSTATE, 0, m2->m_state)) )
			return -EAGAIN;
		freemsg(mp);
		qreply(q, resp);
		return(0);
	}
	switch ( m2->t_state )
	{
		case TS_IDLE:
			if ( !(resp = lmi_ok_ack(LMI_DISABLE_REQ, LMI_DISABLED)) )
				return -EAGAIN;

			m2->m_state = LMI_DISABLED;

			freemsg(mp);
			qreply(q, resp);
			return(0);

		case TS_DATA_XFER:
			if ( !(resp = t_discon_req()) )
				return -EAGAIN;

			m2->t_state = TS_WACK_DREQ9;
			m2->m_state = LMI_DISABLE_PENDING;

			freemsg(mp);
			putnext(q, resp);
			return(0);

		/* FIXME: more state here to check */

		default:
			if ( !(resp = lmi_error_ack(LMI_SYSERR, EFAULT, m2->m_state)) )
				return -EAGAIN;

			freemsg(mp);
			qreply(q, resp);
			return(0);
	}
	if ( !(resp = lmi_error_ack(LMI_OUTSTATE, 0, m2->m_state)) )
		return -EAGAIN;
	freemsg(mp);
	qreply(q, resp);
	return(0);
}
/*
 *  LMI_OPTMGMT_REQ:
 *  -------------------------------------------------------------------------
 */
static int lmi_optmgmt_req(m2pa_t *m2, mblk_t *mp)
{
	mblk_t *resp;
	m2pa_t *m2 = M2PA_PRIV(q);
	lmi_optmgmt_req *p = (lmi_optmgmt_req *)mp->b_rptr;

	caddr_t *opt_ptr = p->lmi_opt;
	size_t  *opt_len = mp->b_wptr - opt_ptr;

	if ( m2->m_state != LMI_UNATTACHED
			&& m2->m_state != LMI_DISABLED
			&& m2->m_state != LMI_ENABLED )
	{
		if ( !(resp = lmi_error_ack(LMI_OUTSTATE, 0, m2->m_state)) )
			return -EAGAIN;
		freemsg(mp);
		qreply(q, resp);
		return(0);
	}
	if ( !opt_len )
	{
		if ( !(resp = lmi_optmgmt_ack(p->lmi_mgmt_flag, NULL, 0)) )
			return -EAGAIN;
		freemsg(mp);
		qreply(q, resp);
		return(0);
	}

	if ( !(resp = lmi_optmgmt_ack(T_FAILURE, opt_ptr, opt_len)) )
		return -EAGAIN;
	/*
	 *  FIXME: pull out options and fill in response
	 *  FIXME: pull out options and fill in response
	 *  FIXME: pull out options and fill in response
	 */
	freemsg(mp);
	qreply(q, resp);
	return(0);
}

/*
 *  =========================================================================
 *
 *  M2PA-Provider --> SL-User PROTOCOL Primitives
 *
 *  -------------------------------------------------------------------------
 *
 *  SL_PDU_REQ
 *  -------------------------------------
 *
 *  This is used to send data coming down in a pdu request.  We strap options
 *  on the M_DATA blocks and pass it on.
 *
 */
static int sl_pdu_req(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	if ( !(mp = m2pa_send_data(pdu->b_cont)) )
		return -EAGAIN;
	freeb(pdu);
	putnext(q, mp);
	return(0);
}
/*
 *  SL_EMERGENCY_REQ
 *  -------------------------------------
 *  We keep track of whether we are in an emergency situation or not so that
 *  we can modify the alignment and proving procedures when they are invoked.
 */
static int sl_emergency_req(queue_t *q, mblk_t *pdu)
{
	M2PA_PRIV(q)->flags |= M2PA_LOC_EMERGENCY;
	freemsg(pdu);
	return(0);
}
/*
 *  SL_EMERGENCY_CEASES_REQ
 *  -------------------------------------
 *  We keep track of whether we are in an emergency situation or not so that
 *  we can modify the alignment and proving procedures when they are invoked.
 */
static int sl_emergency_ceases_req(queue_t *q, mblk_t *pdu)
{
	M2PA_PRIV(q)->flags &= ~M2PA_LOC_EMERGENCY;
	freemsg(pdu);
	return(0);
}
/*
 *  SL_START_REQ
 *  -------------------------------------
 */
static int sl_start_req(queue_t *q, mblk_t *pdu)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);

	switch ( m2->state )
	{
		case M2PA_OUT_OF_SERVICE:
			if ( !(mp = m2pa_send_sio()) )
				return -EAGAIN;

			m2->state = M2PA_NOT_ALIGNED;
			m2->t2_timer = timeout(m2->t2_timeout, (long)m2, m2->t2_value);
			putnext(q, mp);
			break;
	}
	/* ignore multiple start primitives */
	freemsg(pdu);
	return(0);
}
/*
 *  SL_STOP_REQ
 *  -------------------------------------
 */
static int sl_stop_req(queue_t *q, mblk_t *pdu)
{
	switch ( m2->state )
	{
	case M2PA_AIP:
		switch ( m2->t_state )
		{
		case TS_WACK_BREQ:
			return -ERESTART;
		case TS_IDLE:
			if ( t_unbnd_req(m2) == -ENOBUFS )
				return -EAGAIN;
			m2->t_state = TS_WACK_UREQ;
			break;
		case TS_WCON_CREQ:
			if ( t_discon_req(m2) == -ENOBUFS )
				return -EAGAIN;
			m2->t_state = TS_WACK_DREQ6;
			break;
		case TS_WRES_CIND:
			if ( t_discon_req(m2) == -ENOBUFS )
				return -EAGAIN;
			m2->t_state = TS_WACK_DREQ7;
			break;
		case TS_WACK_CREQ:
		case TS_WACK_CRES:
			break;
		}
		m2->state = M2PA_OOS;
		untimeout(m2->t1_timer); m2->t1_timer = 0;
		break;
	case M2PA_INS_LOCAL:
		/* we are always in TS_DATA_XFER state */
		if ( t_ordrel_req(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->t_state = TS_WACK_DREQ9;
		m2->state = M2PA_OOS;
		break;
	case M2PA_INS:
		/* we are always in TS_DATA_XFER state */
		if ( t_ordrel_req(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->t_state = TS_WACK_DREQ9;
		m2->state = M2PA_RETRIEVAL;
		break;
	default:
	case M2PA_IDLE:
	case M2PA_OOS:
	case M2PA_RETRIEVAL:
		/* we are already stopped - ignore */
		break;
	}
	freemsg(mp);
	return(0);
}
/*
 *  SL_RETRIEVE_BSNT_REQ
 *  -------------------------------------
 */
static int sl_retrieve_bsnt_req(queue_t *q, mblk_t *pdu)
{
	/*
	 *  TODO: write this function
	 */
	freemsg(mp);
	return(0);
}
/*
 *  SL_RETRIEVAL_REQUEST_AND_FSNC_REQ
 *  -------------------------------------
 */
static int sl_retrieval_request_and_fsnc_req(queue_t *q, mblk_t *pdu)
{
	m2->fsnc = ((sl_retrieval_req_and_fsnc_t *)mp->b_rptr)->sl_fsnc;
	/*
	 *  TODO: perform retrieval
	 */
	freemsg(mp);
	return(0);
}
/*
 *  SL_RESUME_REQ
 *  -------------------------------------
 */
static int sl_resume_req(queue_t *q, mblk_t *pdu)
{
	/*
	 *  TODO: write this function
	 */
	freemsg(mp);
	return(0);
}
/*
 *  SL_CLEAR_BUFFERS_REQ
 *  -------------------------------------
 */
static int sl_clear_buffers_req(queue_t *q, mblk_t *pdu)
{
	/*
	 *  TODO: write this function
	 */
	freemsg(mp);
	return(0);
}
/*
 *  SL_CLEAR_RTB_REQ
 *  -------------------------------------
 */
static int sl_clear_rtb_req(queue_t *q, mblk_t *pdu)
{
	/*
	 *  TODO: write this function
	 */
	freemsg(mp);
	return(0);
}
/*
 *  SL_LOCAL_PROCESSOR_OUTAGE_REQ
 *  -------------------------------------
 */
static int sl_local_processor_outage_req(queue_t *q, mblk_t *pdu)
{
	if ( !(m2->flags & M2PA_FLAG_LOCAL_PROC_OUTAGE) )
	{
		if ( m2pa_send_sipo(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->flags |= M2PA_FLAG_LOCAL_PROC_OUTAGE;
	}
	freemsg(mp);
	return(0);
}
/*
 *  SL_LOCAL_PROCESSOR_RECOVERED_REQ
 *  -------------------------------------
 */
static int sl_local_processor_recovered_req(queue_t *q, mblk_t *pdu)
{
	if ( m2->flags & M2PA_FLAG_LOCAL_PROC_OUTAGE )
	{
		if ( m2pa_send_not_sipo(m2) == -ENOBUFS )
			return -EAGAIN;
		m2->flags &= ~M2PA_FLAG_LOCAL_PROC_OUTAGE;
	}
	freemsg(mp);
	return(0);
}
/*
 *  SL_CONGESTION_DISCARD_REQ
 *  -------------------------------------
 */
static int sl_congestion_discard_req(queue_t *q, mblk_t *pdu)
{
	if ( !(m2->flags & (M2PA_FLAG_L3_CONG_DISC)) )
	{
		if ( !(m2->flags & (M2PA_FLAG_L2_CONG_DISC)) )
			if ( m2pa_send_sib(m2) == -ENOBUFS )
				return -EAGAIN;
		m2->flags |= M2PA_FLAG_L3_CONG_DISC;
	}
	freemsg(mp);
	return(0);
}
/*
 *  SL_CONGESTION_ACCEPT_REQ
 *  -------------------------------------
 */
static int sl_congestion_accept_req(queue_t *q, mblk_t *pdu)
{
	if ( !(m2->flags & (M2PA_FLAG_L3_CONG_ACPT|S2PA_FLAG_L3_CONG_DISC)) )
	{
		if ( !(m2->flags & (M2PA_FLAG_L2_CONG_ACPT|S2PA_FLAG_L2_CONG_DISC)) )
			if ( m2pa_send_sib(m2) == -ENOBUFS )
				return -EAGAIN;
		m2->flags |= M2PA_FLAG_L3_CONG_ACPT;
	}
	freemsg(mp);
	return(0);
}
/*
 *  SL_NO_CONGESTION_REQ
 *  -------------------------------------
 */
static int sl_no_congestion_req(queue_t *q, mblk_t *pdu)
{
	if ( m2->flags & (M2PA_FLAG_L3_CONG_ACPT|S2PA_FLAG_L3_CONG_DISC) )
	{
		if ( !(m2->flasg & (M2PA_FLAG_L2_CONG_ACPT|S2PA_FLAG_L2_CONG_DISC)) )
			if ( m2pa_send_not_sib(m2) == -ENOBUFS )
				return -EAGAIN;
		m2->flags &= ~M2PA_FLAG_L3_CONG_ACPT;
		m2->flags &= ~M2PA_FLAG_L3_CONG_DISC;
	}
	freemsg(mp);
	return(0);
}
/*
 *  SL_POWER_ON_REQ
 *  -------------------------------------
 */
static int sl_power_on_req(queue_t *q, mblk_t *pdu)
{
	/*
	 *  TODO: write this function
	 */
	freemsg(mp);
	return(0);
}

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

/*
 *  T_INFO_ACK
 *
 *  We might need this if we are using other transports and we send a
 *  T_INFO_REQ to the T-Provider to see what semantics we should use for
 *  encapsulating messages and stuff.  If we are only using SCTP, we know what
 *  to expect and will never see this message.
 */
static int t_info_ack(queue_t *q, mblk_t *mp)
{
	struct T_info_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)q; (void)mp; return(-EPROTO);
}
/*
 *  T_BIND_ACK
 *
 *  SCTP will send us this response when we go to bind the stream for
 *  connection or listening.  We should acknowledge this fact and move to the
 *  next state in attempting to bring up the SCTP association.
 */
static int t_bind_ack(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);

	if ( m2->t_state == TS_WACK_BREQ )
	{
		if ( !(mp = lmi_ok_ack(LMI_ATTACH_REQ, LMI_DISABLED)) )
			return -EAGAIN;
		m2->t_state = TS_IDLE;
		m2->m_state = LMI_DISABLED;
		freemsg(tp);
		putnext(q, mp);
		return(0);
	}
	return(-EPROTO);
}
/*
 *  T_OPTMGMT_ACK
 *
 *  SCTP will send us this repsonse when we use a T_OPTMGMT_REQ to set the
 *  number of inbound and outbound SCTP-streams.  Actually, we never really
 *  need this message (T_CONN_REQ and T_CONN_RES work just fine) so we should
 *  not expect this message.  This is a T-Provider error.  We should M_ERROR
 *  out the stream.
 */
static int t_optmgmt_ack(m2pa_t *m2, mblk_t *mp)
{
	struct T_optmgmt_ack *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}
/*
 *  T_ADDR_ACK
 *
 *  SCTP should no send us this message, because we never send a T_ADDR_REQ to
 *  SCTP, so we do not expect the response.  This is a T-Provider error.  We
 *  should M_ERROR out the queue.
 */
static int t_addr_ack(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	struct T_addr_ack *p = (struct T_addr_ack *)tp->b_rptr;

	const caddr_t ppa_ptr = (caddr_t)(&p->LOCADDR_length);
	const  size_t ppa_len = tp->b_wptr - ppa_ptr;

	p->LOCADDR_offset -= ppa_ptr - tp->b_rptr;
	p->REMADDR_offset -= ppa_ptr - tp->b_rptr;

	if ( !(mp = lmi_info_ack(m2->m_state, ppa_ptr, ppa_len)) )
	{
		p->LOCADDR_offset += ppa_ptr - tp->b_rptr;
		p->REMADDR_offset += ppa_ptr - tp->b_rptr;
		return -EAGAIN;
	}

	freemsg(tp);
	putnext(q, mp);
	return(0);
}
/*
 *  T_OK_ACK
 *
 *  SCTP has acknowledged receipt of our last primitive.  We can move to the
 *  successful state.
 */
static int t_ok_ack(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	struct T_ok_ack *p = (struct T_ok_ack *)tp->b_rptr;

	switch ( m2->t_state )
	{
		case TS_WACK_UREQ:
			if ( !(mp = lmi_ok_ack(LMI_DETACH_REQ, LMI_UNATTACHED)) )
				return -EAGAIN;
			m2->t_state = TS_UNBND;
			m2->m_state = LMI_UNATTACHED;
			freemsg(tp);
			putnext(q, mp);
			return(0);

		case TS_WACK_CREQ:
			m2->t_state = TS_WCON_CREQ;
			/* still waiting for connect */
			freemsg(tp);
			return(0);

		case TS_WACK_CRES:
			if ( !(mp = lmi_enable_con()) )
				return -EAGAIN;
			m2->t_state = TS_DATA_XFER;
			m2->m_state = LMI_ENABLED;
			freemsg(tp);
			putnext(q, mp);
			return(0);

		case TS_WACK_DREQ6:
		case TS_WACK_DREQ7:
		case TS_WACK_DREQ9:
		case TS_WACK_DREQ10:
		case TS_WACK_DREQ11:
			if ( !(mp = lmi_disable_con()) )
				return -EAGAIN;
			m2->t_state = TS_IDLE;
			m2->m_state = LMI_DISABLED;
			freemsg(tp);
			putnext(q, mp);
			return(0);
	}
	return(-EPROTO);
}
/*
 *  T_ERROR_ACK
 *
 *  SCTP has refused our last primitive.  We can either respond with an error
 *  to the M2PA user or we will have to M_ERROR out the stream.
 */
static int t_error_ack(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	struct T_error_ack *p = (struct T_error_ack *)tp->b_rptr;
	int error  = p->TLI_error;
	int reason = p->UNIX_error;

	switch ( m2->t_state )
	{
		case TS_WACK_BREQ:
			/*
			 *  FIXME: should translate error and reason
			 */
			if ( !(mp = lmi_error_ack(LMI_ATTACH_REQ, error, reason, LMI_UNATTACHED)) )
				return -EAGAIN;
			m2->t_state = TS_UNBND;
			m2->m_state = LMI_UNATTACHED;
			freemsg(tp);
			putnext(q, mp);
			return(0);

		case TS_WACK_UREQ:
			/*
			 *  FIXME: should translate error and reason
			 */
			if ( !(mp = lmi_error_ack(LMI_DETACH_REQ, error, reason, LMI_DISABLED)) )
				return -EAGAIN;
			m2->t_state = TS_IDLE;
			m2->m_state = LMI_DISABLED;
			freemsg(tp);
			putnext(q, mp);
			return(0);

		case TS_WACK_CREQ:
			/*
			 *  FIXME: should translate error and reason
			 */
			if ( !(mp = lmi_error_ind(error, reason, LMI_DISABLED)) )
				return -EAGAIN;
			m2->t_state = TS_IDLE;
			m2->m_state = LMI_DISABLED;
			freemsg(tp);
			putnext(q, mp);
			return(0);

		case TS_WACK_CRES:
			/*
			 *  FIXME: should translate error and reason
			 */
			if ( !(mp = lmi_error_ind(error, reason, LMI_DISABLED)) )
				return -EAGAIN;
			m2->t_state = TS_WRES_CIND;
			/* m2->m_state = LMI_ENABLE_PENDING; */
			/* FIXME: this is not a good place to stay */
			freemsg(tp);
			putnext(q, mp);
			return(0);

		case TS_WACK_OPTREQ:
			/* bad doo doo */
			return(-EPROTO);

		case TS_WACK_DREQ6:
		case TS_WACK_DREQ7:
		case TS_WACK_DREQ9:
		case TS_WACK_DREQ10:
		case TS_WACK_DREQ11:
			/* these are all bad */
			return(-EPROTO);
	}
	return(-EPROTO);
}
/*
 *  T_CONN_IND
 *
 *  SCTP has given us a connection indication.  Even though we might not set
 *  the CONIND_number to a positive integer (we set it to zero), we may still
 *  receive a T_CONN_IND if the SCTP association has restarted.  We should
 *  take appropriate action depending on what we think the state of the
 *  association is: if we are waiting to connect we should accept the
 *  connection with a T_CONN_RES with the current stream, or we should refuse
 *  the request with a T_DISCON_REQ.
 */
static int t_conn_ind(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	struct T_conn_ind *p = (struct T_conn_ind *)mp->b_rptr;

	if ( m2->m_state == LMI_ENABLE_PENDING )
	{
		/*
		 *  check if this is our guy, if it isn't reject the
		 *  connection.  careful: watch for restart conditions,
		 *  dual siezures for peer operation.
		 */
	}
	/*
	 *  try to reject the connection indication, we don't want
	 *  to connect until we are enabled.
	 */
}
/*
 *  T_CONN_CON
 *
 *  SCTP has informed us that our connection request was successful.  We
 *  should inform the M2PA user and get ready to transfer data.
 */
static int t_conn_con(m2pa_t *m2, mblk_t *mp)
{
	if ( m2->t_state == TS_WCON_CREQ )
	{
		struct T_conn_con *p = (struct T_conn_con *)mp->b_wptr;

		/*
		 *  TODO: check for 2 streams inbound only.
		 */
		switch ( m2->state )
		{
		case M2PA_AIP:
			m2->state = M2PA_INS_LOCAL;
			m2->t_state = TS_DATA_XFER;
			/*
			 *  TODO: Get SRTT report.
			 */
			if ( mp->b_cont )
				m2pa_recv_data(mp->b_cont);
			if ( srtt <= m2->srtt_max )
			{
				if ( m2pa_send_in_service(m2) == -ENOBUFS )
					return -EAGAIN;
			}
			else
			{
				m2->failure_reason = FIXME;
				t_discon_req(m2);
				m2->t_state = TS_WACK_DREQ9;
				sl_out_of_service(m2);
				m2->state = M2PA_OOS;
			}
			break;
		default:
		case M2PA_IDLE:
		case M2PA_OOS:
		case M2PA_RETRIEVAL:
		case M2PA_INS_LOCAL:
		case M2PA_INS:
			break;
		}
	}
	freemsg(mp);
	return(0);
}
/*
 *  T_DATA_IND
 *
 *  SCTP has given us data on STREAM 0.  This is a control channel for M2PA,
 *  so we should process these messages as control messages.
 */
static int t_data_ind(m2pa_t *m2, mblk_t *mp)
{
	struct T_data_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}
/*
 *  T_EXDATA_IND
 *
 *  SCTP has given us out-of-order data on STREAM 0.  This is a control
 *  channel for M2PA so we should process these messages as control messages.
 */
static int t_exdata_ind(m2pa_t *m2, mblk_t *mp)
{
	struct T_exdata_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}
/*
 *  T_OPTDATA_IND
 *
 *  SCTP has given us data on a specific SCTP-stream.  We should process this
 *  message for the SCTP-stream indicated.
 */
static int t_optdata_ind(m2pa_t *m2, mblk_t *mp)
{
	struct T_optdata_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}
/*
 *  T_OPTDATA_CON (Not in XTI/TLI)
 *
 *  SCTP has given us an indication that an outstanding data message with
 *  given user and provider/peer token values has been acknowledged received
 *  by the peer.  We can strike this message from our retrieval buffers.
 */
static int t_optdata_con(m2pa_t *m2, mblk_t *mp)
{
	struct T_optdata_con *p = (T_optdata_con *)mp->b_rptr;
	mblk_t *dp = (mblk_t *)p->TOKEN_value;
	mblk_t *bp = m2->txq.q_head;

	/* little check just to be sure that it is in the bufq */
	for ( ; bp && bp != dp; bp = bp->next );
	if ( bp != dp ) return (-EPROTO);
	bufq_unlink(&m2->txq, dp);
	m2pa_recalc_congestion(m2);
	freemsg(mp);
	return(0);
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}
/*
 *  T_DISCON_IND
 *
 *  We treat disconnection indications like equipment failure.  It changes the
 *  management state of the interface and management is informed so that the
 *  signalling link might be removed from service or another SDT/SDL assigned.
 *  We do not attempt to automatically restore the connection.  We wait for
 *  management direction or another start indication.
 */
static int t_discon_ind(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	struct T_discon_ind *p = (struct T_discon_ind *)tp->b_wptr;

	const int reason = t_to_sl_reason(p->DISCON_reason);

	if ( !(mp = sl_out_of_service_ind(reason)) )
		return -EAGAIN;
	putnext(q, mp);

	if ( !(mp = lmi_error_ind(LMI_DISC, 0, LMI_DISABLED)) )
		return -EAGAIN;
	putnext(q, mp);

	m2->  state = SL_OUT_OF_SERVICE;
	m2->m_state = LMI_DISABLED;
	m2->t_state = TS_IDLE;
	freemsg(tp);
	return(0);
}
/*
 *  T_ORDREL_IND
 *
 *  We have received an orderly release indication from the peer.  This is
 *  peculiar if we are not expecting it.
 */
static int t_ordrel_ind(queue_t *q, mblk_t *tp)
{
	mblk_t *mp;
	m2pa_t *m2 = M2PA_PRIV(q);
	struct T_ordrel_ind *p = (struct T_ordrel_ind *)tp->b_rptr;

	if ( !(mp = sl_out_of_service_ind(FIXME)) )
		return -EAGAIN;
	putnext(q, mp);


	if ( !(mp = lmi_error_ind(LMI_DISC, 0, LMI_DISABLED)) )
		return -EAGAIN;
	putnext(q, mp);

	m2->  state = SL_OUT_OF_SERVICE;
	m2->m_state = LMI_DISABLED;
	m2->t_state = TS_IDLE;
	freemsg(tp);
	return(0);
}
/*
 *  T_UNITDATA_IND
 *
 *  SCTP should not send us this message; however, we might want to use other
 *  transports for M2PA, so we process this message as well as possible.
 */
static int t_unitdata_ind(m2pa_t *m2, mblk_t *mp)
{
	struct T_unitdata_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}
/*
 *  SCTP should not send us this message; however, we might want to use other
 *  transports for M2PA, so we process this message as well as possible.
 */
static int t_uderror_ind(m2pa_t *m2, mblk_t *mp)
{
	struct T_uderror_ind *p;
	ptrace(("Unexpected primitive received from T-Provider\n"));
	(void)m2; (void)mp; return(-EPROTO);
}

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

/*
 *  =========================================================================
 *
 *  Message Handling
 *
 *  =========================================================================
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_DATA messages are data messages which are passed between protocol
 *  modules or between protocol modules and users.
 *  
 */
static inline int
m2pa_w_data(queue_t *q, mblk_t *mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	return m2pa_send_data(m2, mp);
}
static inline int
m2pa_r_data(queue_t *q, mblk_t *mp)
{
	m2pa_t *m2 = M2PA_PRIV(q);
	return m2pa_recv_data(m2, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO message are normal protocol messages which are passed between
 *  protocol modules or between protocol modules and users.
 */
static inline int
m2pa_w_proto(queue_t *q, mblk_t *mp)
{
	switch ( ((union M2PA_primitives *)(mp->b_rptr))->type )
	{
		case SL_PDU_REQ:			return sl_pdu_req(q, mp);
		case SL_START_REQ:			return sl_start_req(q, mp);
		case SL_STOP_REQ:			return sl_stop_req(q, mp);
		case SL_POWER_ON_REQ:			return sl_power_on_req(q, mp);
	}
	return(-EOPNOTSUPP);
}
static inline int
m2pa_r_proto(queue_t *q, mblk_t *mp)
{
	switch ( ((union T_primitives *)(mp->b_rptr))->type )
	{
		case T_DATA_IND:	return t_data_ind(q, mp);
		case T_EXDATA_IND:	return t_exdata_ind(q, mp);
		case T_OPTDATA_IND:	return t_optdata_ind(q, mp);
		case T_CONN_IND:	return t_conn_ind(q, mp);
		case T_CONN_CON:	return t_conn_con(q, mp);
		case T_DISCON_IND:	return t_discon_ind(q, mp);
		case T_ORDREL_IND:	return t_ordrel_ind(q, mp);
		case T_UNITDATA_IND:	return t_unitdata_ind(q, mp);
		case T_UDERROR_IND:	return t_uderror_ind(q, mp);
	}
	return(-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_PCPROTO messages are priority protocol messages which are passed between
 *  protocol modules or between protocol modules and users.
 */
static inline int
m2pa_w_pcproto(queue_t *q, mblk_t *mp)
{
	switch ( ((union M2PA_primitives *)(mp->b_rptr))->type )
	{

		case SL_EMERGENCY_REQ:			return sl_emergency_req(q, mp);
		case SL_EMERGENCY_CEASES_REQ:		return sl_emergency_ceases_req(q, mp);
		case SL_RETRIEVE_BSNT_REQ:		return sl_retrieve_bsnt_req(q, mp);
		case SL_RETRIEVAL_REQUEST_AND_FSNC_REQ:	return sl_retrieval_request_and_fsnc_req(q, mp);
		case SL_CLEAR_BUFFERS_REQ:		return sl_clear_buffers_req(q, mp);
		case SL_CLEAR_RTB_REQ:			return sl_clear_rtb_req(q, mp);
		case SL_LOCAL_PROCESSOR_OUTAGE_REQ:	return sl_local_processor_outage_req(q, mp);
		case SL_RESUME_REQ:			return sl_resume_req(q, mp);
		case SL_CONGESTION_DISCARD_REQ:		return sl_congestion_discard_req(q, mp);
		case SL_CONGESTION_ACCEPT_REQ:		return sl_congestion_accept_req(q, mp);
		case SL_NO_CONGESTION_REQ:		return sl_no_congestion_req(q, mp);
		case SL_POWER_ON_REQ:			return sl_power_on_req(q, mp);
		case SL_OPTMGMT_REQ:			return sl_optmgmt_req(q, mp);
		case SL_NOTIFY_REQ:			return sl_notify_req(q, mp);

		case LMI_INFO_REQ:			return lmi_info_req(q, mp);
		case LMI_ATTACH_REQ:			return lmi_attach_req(q, mp);
		case LMI_DETACH_REQ:			return lmi_detach_req(q, mp);
		case LMI_ENABLE_REQ:			return lmi_enable_req(q, mp);
		case LMI_DISABLE_REQ:			return lmi_disable_req(q, mp);
		case LMI_OPTMGMT_REQ:			return lmi_optmgmt_req(q, mp);
	}
	return(-EOPNOTSUPP);
}
static inline int
m2pa_r_pcproto(queue_t *q, mblk_t *mp)
{
	switch ( ((union T_primitives *)(mp->b_rptr))->type )
	{
		case T_INFO_ACK:	return t_info_ack(q, mp);
		case T_OPTMGMT_ACK:	return t_optmgmt_ack(q, mp);
		case T_BIND_ACK:	return t_bind_ack(q, mp);
		case T_OK_ACK:		return t_ok_ack(q, mp);
		case T_ERROR_ACK:	return t_error_ack(q, mp);
		case T_ADDR_ACK:	return t_addr_ack(q, mp);
		/* new primitives for M2PA support */
		case T_OPTDATA_CON:	return t_optdata_con(q, mp);
	}
	return(-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling
 *
 *  -------------------------------------------------------------------------
 *
 *  M_CTL messages are used to pass control messages between protocol modules
 *  (not to or from user space).
 */
static inline int
m2pa_w_ctl(queue_t *q, mblk_t *mp)
{
	switch ( ((union M2PA_controls *)(mp->b_wptr))->type )
	{
		case M2PA_CTL_XXXX:	return m2pa_ctl_xxxx(q, mp);
	}
	return(-EOPNOTSUPP);
}
static inline int
m2pa_r_ctl(queue_t *q, mblk_t *mp)
{
	switch ( ((union M2PA_controls *)(mp->b_wptr))->type )
	{
		case T_CTL_XXXX:	return t_ctl_xxxx(q, mp);
	}
	return(-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int
m2pa_do_ioctl(queue_t *q, int cmd, void *arg)
{
	int nr = _IOC_NR(cmd); (void)nr;

	if ( _IOC_TYPE(cmd) == M2PA_IOC_MAGIC )
		switch ( cmd )
		{
			case M2PA_IOCXXXXX:	return m2pa_iocxxxx(q, cmd, arg);
			case M2PA_IOCXXXXX:	return m2pa_iocxxxx(q, cmd, arg);
			case M2PA_IOCXXXXX:	return m2pa_iocxxxx(q, cmd, arg);
			case M2PA_IOCXXXXX:	return m2pa_iocxxxx(q, cmd, arg);
		}
	return -ENXIO;
}
static inline 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;
	int ret = -EINVAL;
	struct linkblk *lp = (struct linkblk *)arg;

	switch ( cmd )
	{
		case I_STR:
			if ( count >= _IOC_SIZE(cmd) )
			{
				ret = m2pa_do_ioctl(q, cmd, arg);
			}
			if ( ret == -ENXIO )
			{
				if ( q->q_next )
				{
					putnext(q, mp);
					return(0);
				}
			}
			break;
		default:
		case I_LINK:
		case I_PLINK:
		case I_UNLINK:
		case I_PUNLINK:
			ret = -EINVAL;
			break;
	}
	if ( ret == 0 )
	{
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval	= 0;
	}
	else
	{
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval  = -1;
	}
	qreply(q, mp);
	return(0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void
m2pa_w_flush(queue_t *q, mblk_t *mp)
{
	if ( *mp->b_rptr & 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_PRIV(q);
	m2pa->state = M2PA_MERROR;
	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
 *
 *  -------------------------------------------------------------------------
 *
 *  SCTP 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_PRIV(q);
	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);

	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_alloc_priv(q)) )
			return ENOMEM;
		return(0);
	}
	return EIO;
}

/*
 *  -------------------------------------------------------------------------
 *
 *  CLOSE - Last close
 *
 *  -------------------------------------------------------------------------
 */
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
 *
 *  =========================================================================
 */

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)
{
	sctp_terminate();
	return;
}
#endif
