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

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

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

 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:03 by brian

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

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

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

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

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

#ifndef INT
#define INT void
#endif

typedef void (*bufcall_fnc_t) (long);

/* 
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info m2pa_info = {
	0,			/* Module ID number */
	"m2pa-sdt",		/* Module name */
	0,			/* Min packet size accepted *//* FIXME */
	INFPSZ,			/* Max packet size accepted *//* FIXME */
	1 << 15,		/* Hi water mark *//* FIXME */
	1 << 10			/* Lo water mark *//* FIXME */
};

static int m2pa_open(queue_t *, dev_t *, int, int, cred_t *);
static int m2pa_close(queue_t *, int, cred_t *);

static INT m2pa_rput(queue_t *, mblk_t *);
static INT m2pa_rsrv(queue_t *);

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

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

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

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

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

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

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

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

/* 
 *  =========================================================================
 *
 *  SDT Provider (M2PA) -> DTL User Primitives
 *
 *  =========================================================================
 */
/* 
 *  LMI_INFO_ACK
 *  ---------------------------------------------
 */
static int lmi_info_ack(m2pa_t * m2)
{
	mblk_t *mp;
	lmi_info_ack_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_info_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_INFO_ACK;
		p->lmi_version = 1;
		p->lmi_state = m2->state;
		p->lmi_max_sdu = -1;
		p->lmi_min_sdu = 0;
		p->lmi_header_len = 0;
		p->lmi_ppa_style = LMI_STYLE1;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_OK_ACK
 *  ---------------------------------------------
 */
static int lmi_ok_ack(m2pa_t * m2, long prim)
{
	mblk_t *mp;
	lmi_ok_ack_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_ok_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_OK_ACK;
		p->lmi_correct_primitive = prim;
		switch (m2->state)
		{
		case LMI_ATTACH_PENDING:
			m2->state = LMI_DISABLED;
			break;
		case LMI_DETACH_PENDING:
			m2->state = LMI_UNATTACHED;
			break;
			/* default is don't change state */
		}
		p->lmi_state = m2->state;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_ERROR_ACK
 *  ---------------------------------------------
 */
static int lmi_error_ack(m2pa_t * m2, long prim, long err)
{
	mblk_t *mp;
	lmi_error_ack_t *p;
	switch (err)
	{
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (err);
	}
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_ERROR_ACK;
		p->lmi_errno = err < 0 ? -err : 0;
		p->lmi_reason = err < 0 ? LMI_SYSERR : err;
		p->lmi_error_primitive = prim;
		switch (m2->state)
		{
		case LMI_ATTACH_PENDING:
			m2->state = LMI_UNATTACHED;
			break;
		case LMI_DETACH_PENDING:
			m2->state = LMI_DISABLED;
			break;
		case LMI_ENABLE_PENDING:
			m2->state = LMI_DISABLED;
			break;
		case LMI_DISABLE_PENDING:
			m2->state = LMI_ENABLED;
			break;
			/* 
			 *  Default is not to change state.
			 */
		}
		p->lmi_state = m2->state;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_ENABLE_CON
 *  ---------------------------------------------
 */
static int lmi_enable_con(m2pa_t * m2)
{
	mblk_t *mp;
	lmi_enable_con_t *p;
	ensure(m2->state == LMI_ENABLE_PENDING, return (-EFAULT));
	if (canputnext(m2->rq))
	{
		if ((mp = allocb(sizeof(*p), BPRI_MED)))
		{
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_enable_con_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_ENABLE_CON;
			p->lmi_state = m2->state = LMI_ENABLED;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

/* 
 *  LMI_DISABLE_CON
 *  ---------------------------------------------
 */
static int lmi_disable_con(m2pa_t * m2)
{
	mblk_t *mp;
	lmi_disable_con_t *p;
	ensure(m2->state == LMI_DISABLE_PENDING, return (-EFAULT));
	if (canputnext(m2->rq))
	{
		if ((mp = allocb(sizeof(*p), BPRI_MED)))
		{
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_disable_con_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_DISABLE_CON;
			p->lmi_state = m2->state = LMI_DISABLED;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

#if 0
/* 
 *  LMI_OPTMGMT_ACK
 *  ---------------------------------------------
 */
static int lmi_optmgmt_ack(m2pa_t * m2, ulong flags, void *opt_ptr, size_t opt_len)
{
	mblk_t *mp;
	lmi_optmgmt_ack_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_optmgmt_ack_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_OPTMGMT_ACK;
		p->lmi_opt_length = opt_len;
		p->lmi_opt_offset = opt_len ? sizeof(*p) : 0;
		p->lmi_mgmt_flags = flags;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}
#endif
/* 
 *  LMI_ERROR_IND
 *  ---------------------------------------------
 */
static int lmi_error_ind(m2pa_t * m2, long err)
{
	mblk_t *mp;
	lmi_error_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = ((lmi_error_ind_t *) mp->b_wptr)++;
		p->lmi_primitive = LMI_ERROR_IND;
		p->lmi_errno = err < 0 ? -err : 0;
		p->lmi_reason = err < 0 ? LMI_SYSERR : err;
		p->lmi_state = m2->state;
		putnext(m2->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/* 
 *  LMI_STATS_IND
 *  ---------------------------------------------
 */
static int lmi_stats_ind(m2pa_t * m2, ulong interval, ulong timestamp)
{
	mblk_t *mp;
	lmi_stats_ind_t *p;
	if (bcanputnext(m2->rq, mp->b_band))
	{
		if ((mp = allocb(sizeof(*p), BPRI_MED)))
		{
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_stats_ind_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_STATS_IND;
			p->lmi_interval = interval;
			p->lmi_timestamp = timestamp;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/* 
 *  LMI_EVENT_IND
 *  ---------------------------------------------
 */
static int lmi_event_ind(m2pa_t * m2, ulong oid, ulong severity, ulong timestamp)
{
	mblk_t *mp;
	lmi_event_ind_t *p;
	if (bcanputnext(m2->rq, mp->b_band))
	{
		if ((mp = allocb(sizeof(*p), BPRI_MED)))
		{
			mp->b_datap->db_type = M_PROTO;
			p = ((lmi_event_ind_t *) mp->b_wptr)++;
			p->lmi_primitive = LMI_EVENT_IND;
			p->lmi_objectid = oid;
			p->lmi_timestamp = timestamp;
			p->lmi_severity = severity;
			putnext(m2->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	seldom();
	return (-EBUSY);
}

/* 
 *  SDT_RC_SIGNAL_UNIT_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_signal_unit_ind(void)
{
	mblk_t *mp;
	sdt_rc_signal_unit_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_signal_unit_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_RC_SIGNAL_UNIT_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_RC_CONGESTION_ACCEPT_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_congestion_accept_ind(void)
{
	mblk_t *mp;
	sdt_rc_congestion_accept_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_congestion_accept_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_RC_CONGESTION_ACCEPT_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_RC_CONGESTION_DISCARD_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_congestion_discard_ind(void)
{
	mblk_t *mp;
	sdt_rc_congestion_discard_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_congestion_discard_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_RC_CONGESTION_DISCARD_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_RC_NO_CONGESTION_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_rc_no_congestion_ind(void)
{
	mblk_t *mp;
	sdt_rc_no_congestion_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_rc_no_congestion_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_RC_CONGESTION_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_IAC_CORRECT_SU_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_iac_correct_su_ind(void)
{
	mblk_t *mp;
	sdt_iac_correct_su_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_iac_correct_su_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_IAC_CORRECT_SU_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_IAC_ABORT_PROVING_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_iac_abort_proving_ind(void)
{
	mblk_t *mp;
	sdt_iac_abort_proving_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_iac_abort_proving_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_IAC_ABORT_PROVING_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_LSC_LINK_FAILURE_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_lsc_link_failure_ind(void)
{
	mblk_t *mp;
	sdt_lsc_link_failure_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_lsc_link_failure_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_LSC_LINK_FAILURE_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  SDT_TXC_TRANSMISSION_REQUEST_IND
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *sdt_txc_transmission_request_ind(void)
{
	mblk_t *mp;
	sdt_txc_transmission_request_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (sdt_txc_transmission_request_ind_t *) mp->b_wptr;
		p->sdt_primitive = SDT_TXC_TRANSMISSION_REQUEST_IND;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  =========================================================================
 *
 *  M2PA SL-Provider --> T-Provider (SCTP) Primitives (M_CTL, M_PROTO, M_PCPROTO)
 *
 *  =========================================================================
 */
/* 
 *  T_INFO_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_info_req(void)
{
	mblk_t *mp;
	struct T_info_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_info_req *)mp->b_wptr;
		p->PRIM_type = T_INFO_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_ADDR_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_addr_req(void)
{
	mblk_t *mp;
	struct T_addr_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_addr_req *)mp->b_wptr;
		p->PRIM_type = T_ADDR_REQ;
		p->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_BIND_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_bind_req(add_ptr, add_len, cons)
	const caddr_t add_ptr;
	const size_t add_len;
	const uint cons;
{
	mblk_t *mp;
	struct T_bind_req *p;

	if ((mp = allocb(sizeof(*p) + add_len, BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_bind_req *)mp->b_wptr;
		p->PRIM_type = T_BIND_REQ;
		p->ADDR_length = add_len;
		p->ADDR_offset = add_len ? sizeof(*p) : 0;
		p->CONIND_number = cons;
		mp->b_wptr += sizeof(*p);
		bcopy(add_ptr, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	return (mp);
}

/* 
 *  T_UNBIND_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_unbind_req(void)
{
	mblk_t *mp;
	struct T_unbind_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_unbind_req *)mp->b_wptr;
		p->PRIM_type = T_UNBIND_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_OPTMGMT_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_optmgmt_req(opt_ptr, opt_len, flags)
	const caddr_t opt_ptr;
	const size_t opt_len;
	const uint flags;
{
	mblk_t *mp;
	struct T_optmgnt_req *p;
	if ((mp = allocb(sizeof(*p) + opt_len, BPRI_MED)))
	{
		mp->b_datap->db_type = M_PCPROTO;
		p = (struct T_optmgmt_req *)mp->b_wptr;
		p->PRIM_type = T_OPMGMT_REQ;
		p->OPT_length = opt_len;
		p->OPT_offset = sizeof(*p);
		p->MGMT_flags = flags;
		mp->b_wptr += sizeof(*p);
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
	}
	return (mp);
}

/* 
 *  T_CONN_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_conn_req(dst_ptr, dst_len, opt_ptr, opt_len)
	const caddr_t dst_ptr;
	const size_t dst_len;
	const caddr_t opt_ptr;
	const size_t opt_len;
{
	mblk_t *mp;
	struct T_conn_req *p;

	if ((mp = allocb(sizeof(*p) + dst_len + opt_len, BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_conn_req *)mp->b_ptpr;
		p->PRIM_type = T_CONN_REQ;
		p->DEST_length = dst_len;
		p->DEST_offset = dst_len ? sizeof(*p) : 0;
		p->OPT_length = opt_len;
		p->OPT_offset = opt_len ? sizeof(*p) + dst_len : 0;
		mp->b_wptr += sizeof(*p);
		bcopy(dst_ptr, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
	}
	return (mp);
}

/* 
 *  T_CONN_RES
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_conn_res(aq, opt_ptr, opt_len, seq)
	const queue_t *aq;
	const caddr_t opt_ptr;
	const size_t opt_len;
	const uint seq;
{
	mblk_t *mp;
	struct T_conn_res *p;
	if ((mp = allocb(sizeof(*p) + opt_len, BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_conn_res *)mp->b_wptr;
		p->PRIM_type = T_CONN_RES;
		p->QUEUE_ptr = q;
		p->OPT_length = opt_len;
		p->OPT_offset = sizeof(*p);
		p->SEQ_number = seq;
		mp->b_wptr += sizeof(*p);
		bopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b - wptr += opt_len;
	}
	return (mp);
}

/* 
 *  T_DISCON_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_discon_req(seq)
	const uint seq;
{
	mblk_t *mp;
	struct T_discon_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_discon_req *)mp->b_wptr;
		p->PRIM_type = T_DISCON_REQ;
		p->SEQ_number = seq;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_ORDREL_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_ordrel_req(void)
{
	mblk_t *mp;
	struct T_ordrel_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_ordrel_req *)mp->b_wptr;
		p->PRIM_type = T_ORDREL_REQ;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_DATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_data_req(void)
{
	mblk_t *mp;
	struct T_data_req *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_data_req *)mp->b_wptr;
		p->PRIM_type = T_DATA_REQ;
		p->MORE_flag = 0;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_EXDATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_exdata_req(void)
{
	mblk_t *mp;
	struct T_exdata_ind *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED)))
	{
		mp->b_datap->db_type = M_PROTO;
		p = (struct T_exdata_req *)mp->b_wptr;
		p->PRIM_type = T_EXDATA_REQ;
		p->MORE_flag = 0;
		mp->b_wptr += sizeof(*p);
	}
	return (mp);
}

/* 
 *  T_OPTDATA_REQ
 *  -------------------------------------------------------------------------
 */
static inline mblk_t *t_optdata_req(uint sid)
{
	mblk_t *mp;
	struct T_optdata_req *p;
	struct t_opthdr *oh;
	const size_t opt_len =
		PAD4(sizeof(*oh) + sizeof(u16)) + PAD4(sizeof(*oh) + sizeof(u32));

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

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

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

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

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

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

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

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

/* 
 *  M2PA_STATUS_BUSY
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_busy(queue_t * q)
{
	return m2pa_send_status(q, M2PA_STATUS_BUSY);
}

/* 
 *  M2PA_STATUS_NOT_BUSY
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_not_busy(queue_t * q)
{
	return m2pa_send_status(q, M2PA_STATUS_NOT_BUSY);
}

/* 
 *  M2PA_STATUS_PROCESSOR_OUTAGE
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_processor_outage(queue_t * q)
{
	return m2pa_send_status(q, M2PA_STATUS_PROCESSOR_OUTAGE);
}

/* 
 *  M2PA_STATUS_NOT_PROCESSOR_OUTAGE
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_not_processor_outage(queue_t * q)
{
	return m2pa_send_status(q, M2PA_STATUS_NOT_PROCESSOR_OUTAGE);
}

/* 
 *  M2PA_STATUS_IN_SERVICE
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_status_in_service(queue_t * q)
{
	return m2pa_send_status(q, M2PA_STATUS_IN_SERVICE);
}

/* 
 *  M2PA_SEND_DATA
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_send_data(queue_t * q, u16 sls, mblk_t * dp)
{
	mblk_t *mp;

	if ((mp = t_optdata_req(sls + 1)))
	{
		mp->b_cont = dp;
		putnext(WR(q), mp);
		return (0);
	}
	return -ENOBUFS;
}

/* 
 *  =========================================================================
 *
 *  M2PA State Machine TIMEOUTS
 *
 *  =========================================================================
 */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/* 
 *  =========================================================================
 *
 *  M2PA STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_data(queue_t * q, mblk_t * mp)
{
	return m2pa_send_data(q, 0, mp);
}
static inline int m2pa_r_data(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = (m2pa_t *) q->q_ptr;
	return m2pa_recv_data(q, m2->sid, mp);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_proto(queue_t * q, mblk_t * mp)
{
	int prim = ((union SDT_primitives *)(mp->b_rptr))->sdt_primitive - SDT_DSTR_FIRST;
	if (0 <= prim && prim <= SDT_DSTR_LAST - SDT_DSTR_FIRST)
		if (sdt_prim[prim])
			return (*sdt_prim[prim]) (q, mp);
	return (-EOPNOTSUPP);
}
static inline int m2pa_r_proto(queue_t * q, mblk_t * mp)
{
	int prim = ((union T_primitives *)(mp->b_rptr))->type;
	if (0 <= prim && prim < sizeof(n_prim) / sizeof(void *))
		if (t_prim[prim])
			return (*t_prim[prim]) (q, mp);
	return (-EOPNOTSUPP);
}
static inline int m2pa_w_pcproto(queue_t * q, mblk_t * mp)
{
	return m2pa_w_proto(q, mp);
}
static inline int m2pa_r_pcproto(queue_t * q, mblk_t * mp)
{
	return m2pa_r_proto(q, mp);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_ctl(queue_t * q, mblk_t * mp)
{
	(void)q;
	(void)mp;
	return (-EOPNOTSUPP);
}
static inline int m2pa_r_ctl(queue_t * q, mblk_t * mp)
{
	(void)q;
	(void)mp;
	return (-EOPNOTSUPP);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline int m2pa_w_ioctl(queue_t * q, mblk_t * mp)
{
	int ret;
	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
	int cmd = iocp->ioc_cmd;
	void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL;
	int type = _IOC_TYPE(cmd);
	int nr = _IOC_NR(cmd);
	int size = _IOC_SIZE(cmd);

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

	case M2PA_IOC_MAGIC:
		if (iocp->ioc_count >= size)
		{
			if (0 <= nr && nr < sizeof(m2pa_iindent: Standard input:1334: Error:Stmt nesting error.
octl) / sizeof(void *))
			{
				if (m2pa_ioctl[nr])
					ret = (*m2pa_ioctl[nr]) (q, cmd, arg);
				else
					ret = -EOPNOTSUPP;
			}
		} else
			return = -EINVAL;
		break;

	default:
		if (q->q_next)
		{
			putnext(q, mp);
			return (0);
		}
		ret = -EOPNOTSUPP;
	}
}

mp->b_datap->db_type = ret ? M_IOCNAK : M_IOCACK;
iocp->ioc_error = -ret;
iocp->ioc_rval = ret ? -1 : 0;
qrepy(q, mp);
return (0);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void m2pa_w_flush(queue_t * q, mblk_t * mp)
{
	if (*mp->b_rptr & M_FLUSHW)
	{
		flushq(q, FLUSHALL);
		if (q - q_next)
		{
			putnext(q, mp);
			return;
		}
		*mp->b_rptr &= ~FLUSHW;
	}
	if (*mp->b_rptr & FLUSHR)
	{
		flushq(RD(q), FLUSHALL);
		qreply(q, mp);
		return;
	}
	if (q->q_next)
	{
		putnext(q, mp);
		return;
	}
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static inline void m2pa_r_error(queue_t * q, mblk_t * mp)
{
	m2pa_t *m2 = (m2pa_t *) q->q_ptr;
	m2->zapped = 1;
	if (q->q_next)
	{
		putnext(q, mp);
		return;
	}
}

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

	if (mp->b_datap->db_type < QPCTL && q->q_count)
	{
		putq(q, mp);
		/* 
		 *  NOTE:- after placing messages on the queue here, I should
		 *  check if placing the message on the queue crosses a band
		 *  threshold for congestion accept and congestion discard.
		 *  When crossing congestion accept, I should sent busy to the
		 *  peer and notify MTP3.  When crossing congestion discard I
		 *  should notify MTP3.
		 */
		return (0);
	}
	switch (mp->b_datap->db_type)
	{
	case M_DATA:
		if ((err = m2pa_r_data(q, mp)))
			break;
		return (0);
	case M_PROTO:
		if ((err = m2pa_r_proto(q, mp)))
			break;
		return (0);
	case M_PCPROTO:
		if ((err = m2pa_r_pcproto(q, mp)))
			break;
		return (0);
	case M_CTL:
		if ((err = m2pa_r_ctl(q, mp)))
			break;
		return (0);
	case M_ERROR:
		m2pa_r_error(q, mp);
		return (0);
	}
	switch (err)
	{
	case -EAGAIN:
		putq(q, mp);
		return (0);
	case -EOPNOTSUPP:
		if (q->q_next)
		{
			putnext(q, mp);
			return (0);
		}
	}
	freemsg(mp);
	return (err);
}

/* 
 *  M2PA RSRV - Queued message from below.
 *
 *  If the message is a priority message we attempt to process it immediately
 *  and without flow control.  If the message is a non-priority message and
 *  the next queue is flow controlled, we put the message back on the queue
 *  and wait.  If we cannot process a priority message immediately we cannot
 *  place it back on the queue and discard it.  We requeue non-priority
 *  messages which cannot be processed immediately.  Unrecognized messages are
 *  passed down-queue.
 */
static INT m2pa_rsrv(queue_t * q)
{
	mblk_t *mp;
	int err = -EOPNOTSUPP;

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

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

/* 
 *  M2PA WSRV = Queued message from above.
 *
 *  If the message is a priority message we attempt to process it immediately
 *  and without flow control.  If the message is a non-priority message and
 *  the next queue is flow controlled, we put the message back on the queue
 *  and wait.  If we cannot process a priority message immediately we cannot
 *  place it back on the queue, we discard it.  We requeue non-priority
 *  messages which cannot be processed immediately.  Unrecognized messages are
 *  passed down-queue.
 */
static INT m2pa_wsrv(queue_t * q)
{
	int err = -EOPNOTSUPP;
	mblk_t *mp;

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


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

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

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

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

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


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

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

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

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

	(void)flag;
	(void)crp;

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

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

static int m2pa_initialized = 0;

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

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

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

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