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

 @(#) m2pa.c,v SS7preBetaRelease(0.8.2.1) 2002/04/06 05:35:02

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

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

 All Rights Reserved.

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

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

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

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

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

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

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

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

 Last Modified 2002/04/06 05:35:02 by brian

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

#ident "@(#) m2pa.c,v SS7preBetaRelease(0.8.2.1) 2002/04/06 05:35:02"

static char const ident[] =
	"m2pa.c,v SS7preBetaRelease(0.8.2.1) 2002/04/06 05:35:02";

#define M2PA_DESCRIP	"M2PA/SCTP STREAMS MODULE."
#define M2PA_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  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
