OpenSS7
SS7 for the
Common Man

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified:

Home Overview Status News Documentation Resources About
   
 Overview
 Status
 News
 Documentation
 Resources
 About

   
Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sigtran/m3ua_n.c


File /code/strss7/drivers/sigtran/m3ua_n.c



#ident "@(#) $RCSfile: m3ua_n.c,v $ $Name:  $($Revision: 0.8.2.3 $) $Date: 2003/04/14 12:13:19 $"

static char const ident[] =
    "$RCSfile: m3ua_n.c,v $ $Name:  $($Revision: 0.8.2.3 $) $Date: 2003/04/14 12:13:19 $";

#include <linux/config.h>
#include <linux/version.h>
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>

#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>
#include <sys/dki.h>

#include <sys/npi.h>
#include <sys/npi_mtp.h>

#include "../lock.h"
#include "../debug.h"
#include "../bufq.h"

#include "m3ua_n.h"

#define M3UA_DESCRIP	"M3UA STREAMS MULTIPLEXING DRIVER -- NPI INTERFACE." "\n" \
			"Part of the OpenSS7 Stack for LiS STREAMS."
#define M3UA_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corp.  All Rights Reserved"
#define M3UA_DEVICE	"Supports OpenSS7 MTP Level 3 Drivers."
#define M3UA_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define M3UA_LICENSE	"GPL"
#define M3UA_BANNER	M3UA_DESCRIP	"\n" \
			M3UA_COPYRIGHT	"\n" \
			M3UA_DEVICE	"\n" \
			M3UA_CONTACT	"\n"

MODULE_AUTHOR(M3UA_CONTACT);
MODULE_DESCRIPTION(M3UA_DESCRIP);
MODULE_SUPPORTED_DEVICE(M3UA_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(M3UA_LICENSE);
#endif

/* 
 *  =========================================================================
 *
 *  NPI Provider (M3UA) -> NPI User Primitives
 *
 *  =========================================================================
 */
/* 
 *  N_CONN_IND          11 - Incoming connection indication
 *  -------------------------------------------------------------
 */
STATIC INLINE int n_conn_ind(queue_t * q, mblk_t * cp)
{
	(void) q;
	(void) cp;
	ptrace(("SWERR: invalid connection indication\n"));
	return (-EFAULT);
}

/* 
 *  N_CONN_CON          12 - Connection established
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_conn_con(queue_t * q, uint pcls, uint flags, m3ua_addr_t * res, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_conn_con_t *p;
	N_qos_sel_conn_mtp_t *qos;
	size_t res_len = res ? sizeof(*res) + res->alen : 0;
	size_t qos_len = sizeof(*qos);
	size_t msg_len = sizeof(*p) + PAD4(res_len) + qos_len;

	if ((sp->i_state != NS_WCON_CREQ))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;		/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_CONN_CON;
	p->RES_length = res_len;
	p->RES_offset = res_len ? sizeof(*p) : 0;
	p->CONN_flags = flags;
	p->QOS_length = qos_len;
	p->QOS_offset = qos_len ? sizeof(*p) + PAD4(res_len) : 0;
	if (res_len) {
		bcopy(res, mp->b_wptr, res_len);
		mp->b_wptr += PAD4(res_len);
	}
	if (qos_len) {
		qos = ((typeof(qos)) mp->b_wptr)++;
		qos->n_qos_type = N_QOS_SEL_CONN_MTP;
	}
	mp->b_cont = dp;
	sp->i_state = NS_DATA_XFER;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: confirmation in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_DISCON_IND        13 - NC disconnected
 *  ---------------------------------------------------------
 *  For M3UA the responding address in a NC connection refusal is always the
 *  destination address (after GTT) to which the NC connection was sent.
 */
STATIC INLINE int
n_discon_ind(queue_t * q, ulong orig, long reason, m3ua_addr_t * res, mblk_t * seq, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_discon_ind_t *p;
	size_t res_len = res ? sizeof(*res) + res->alen : 0;
	size_t msg_len = sizeof(*p) + PAD4(res_len);

	if ((1 << sp->i_state) & ~(NSF_WCON_CREQ | NSF_WRES_CIND
				   | NSF_DATA_XFER | NSF_WCON_RREQ | NSF_WRES_RIND))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_DISCON_IND;
	p->DISCON_orig = orig;
	p->DISCON_reason = reason;
	p->RES_length = res_len;
	p->RES_offset = res_len ? sizeof(*p) : 0;
	p->SEQ_number = (ulong) seq;
	if (res_len) {
		bcopy(res, mp->b_wptr, res_len);
		mp->b_wptr += PAD4(res_len);
	}
	if (seq) {
		bufq_unlink(&sp->conq, seq);
		freemsg(seq);
	}
	sp->i_state = NS_IDLE;
	mp->b_cont = dp;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_DATA_IND          14 - Incoming connection-mode data indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int n_data_ind(queue_t * q, uint more, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_data_ind_t *p;
	N_qos_sel_data_mtp_t *qos;
	const size_t qos_len = sizeof(*qos);
	const size_t msg_len = sizeof(*p) + qos_len;

	if ((1 << sp->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_DATA_IND;
	p->DATA_xfer_flags = (more ? N_MORE_DATA_FLAG : 0);
	qos = ((typeof(qos)) mp->b_wptr)++;
	qos->n_qos_type = N_QOS_SEL_DATA_MTP;
	qos->sls = sp->sls;	/* FIXME */
	qos->mp = sp->mp;	/* FIXME */
	fixme(("Get the SLS and MP from the arguments to this function\n"
	       "Actually, it may be worthwhile not delivering QOS\n"));
	mp->b_cont = dp;
	putnext(sp->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_EXDATA_IND        15 - Incoming expedited data indication
 *  -----------------------------------------------------------
 *  The lack of a more flag presents a challenge.  Perhaps we shouldbe
 *  reassembling expedited data.
 */
STATIC INLINE int n_exdata_ind(queue_t * q, uint more, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_exdata_ind_t *p;
	N_qos_sel_data_mtp_t *qos;
	const size_t qos_len = sizeof(*qos);
	const size_t msg_len = sizeof(*p) + qos_len;

	if ((1 < sp->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;		/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_EXDATA_IND;
	qos = ((typeof(qos)) mp->b_wptr)++;
	qos->n_qos_type = N_QOS_SEL_DATA_MTP;
	qos->sls = sp->sls;	/* FIXME */
	qos->mp = sp->mp;	/* FIXME */
	fixme(("Get the SLS and MP from the arguments to this function\n"
	       "Actually, it may be worthwhile not delivering QOS\n"));
	mp->b_cont = dp;
	mp->b_cont = dp;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_INFO_ACK          16 - Information Acknowledgement
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_info_ack(queue_t * q)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_info_ack_t *p;
	N_qos_sel_info_mtp_t *qos;
	N_qos_range_info_mtp_t *qor;
	m3ua_addr_t *add, *ss = &sp->src;
	uint32_t *pro;
	size_t add_len = ss ? sizeof(*ss) + ss->alen : 0;
	size_t qos_len = sizeof(*qos);
	size_t qor_len = sizeof(*qor);
	size_t pro_len = sizeof(uint32_t);
	size_t msg_len = sizeof(*p) + add_len + qos_len + qor_len + pro_len;

	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_INFO_ACK;
	p->NSDU_size = -1;	/* no limit on NSDU size */
	p->ENSDU_size = -1;	/* no limit on ENSDU size */
	p->CDATA_size = -1;	/* no limit on CDATA size */
	p->DDATA_size = -1;	/* no limit on DDATA size */
	p->ADDR_size = sizeof(m3ua_addr_t) + M3UA_MAX_ADDR_LENGTH;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->QOS_length = qos_len;
	p->QOS_offset = qos_len ? sizeof(*p) + add_len : 0;
	p->QOS_range_length = qor_len;
	p->QOS_range_offset = qor_len ? sizeof(*p) + add_len + qos_len : 0;
	p->OPTIONS_flags = REC_CONF_OPT
	    | EX_DATA_OPT | ((sp->flags & M3UA_FLAG_DEFAULT_RC_SEL) ? DEFAULT_RC_SEL : 0);
	p->NIDU_size = -1;
	p->SERV_type = N_CONS | N_CLNS;
	p->CURRENT_state = sp->i_state;
	p->PROVIDER_type = N_SUBNET;
	p->NODU_size = 254;
	p->PROTOID_length = pro_len;
	p->PROTOID_offset = pro_len ? sizeof(*p) + add_len + qos_len + qor_len : 0;
	p->NPI_version = N_VERSION_2;
	if (add_len) {
		add = (typeof(add)) mp->b_wptr;
		bcopy(ss, add, sizeof(*ss) + ss->alen);
		mp->b_wptr += add_len;
	}
	if (qos_len) {
		qos = ((typeof(qos)) mp->b_wptr)++;
		qos->n_qos_type = N_QOS_SEL_INFO_MTP;
		qos->pvar = sp->pvar;
		qos->popt = sp->popt;
	}
	if (qor_len) {
		qor = ((typeof(qor)) mp->b_wptr)++;
		qor->n_qos_type = N_QOS_RANGE_INFO_M3UA;
		qor->sls_range = 0xff;	/* FIXME: depends on pvar */
		qor->mp_range = 0x03;	/* FIXME: depends on pvar and popt */
	}
	if (pro_len) {
		pro = ((typeof(pro)) mp->b_wptr)++;
		*pro = 3;
	}
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_BIND_ACK          17 - NS User bound to network address
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_bind_ack(queue_t * q)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_bind_ack_t *p;
	m3ua_addr_t *add, *ss = &sp->src;
	uint32_t *pro;
	size_t add_len = ss ? sizeof(*ss) + ss->alen : 0;
	size_t pro_len = sizeof(uint32_t);
	size_t msg_len = sizeof(*p) + add_len + pro_len;

	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_BIND_ACK;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->CONIND_number = sp->conind;
	p->TOKEN_value = (ulong) sp->rq;
	p->PROTOID_length = pro_len;
	p->PROTOID_offset = pro_len ? sizeof(*p) + add_len : 0;
	if (add_len) {
		add = (typeof(add)) mp->b_wptr;
		bcopy(ss, add, sizeof(*ss) + ss->alen);
		mp->b_wptr += add_len;
	}
	if (pro_len) {
		pro = ((typeof(pro)) mp->b_wptr)++;
		*pro = 3;
	}
	sp->i_state = NS_IDLE;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_ERROR_ACK         18 - Error Acknowledgement
 *  ---------------------------------------------------------
 */
STATIC int n_error_ack(queue_t * q, int prim, int err)
{
	m3ua_t *sp = M3UA_PRIV(q);
	mblk_t *mp;
	N_error_ack_t *p;
	const size_t msg_len = sizeof(*p);

	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
	case 0:
		ptrace(("ERROR: No error condition\n"));
		return (err);
	}
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_ERROR_ACK;
	p->ERROR_prim = prim;
	p->NPI_error = err < 0 ? NSYSERR : err;
	p->UNIX_error = err < 0 ? -err : 0;
	switch (sp->i_state) {
	case NS_WACK_OPTREQ:
	case NS_WACK_UREQ:
	case NS_WCON_CREQ:
		sp->i_state = NS_IDLE;
		break;
	case NS_WCON_RREQ:
		sp->i_state = NS_DATA_XFER;
		break;
	case NS_WACK_BREQ:
		sp->i_state = NS_UNBND;
		break;
	case NS_WACK_DREQ6:
		sp->i_state = NS_WCON_CREQ;
		break;
	case NS_WACK_DREQ9:
		sp->i_state = NS_DATA_XFER;
		break;
	case NS_WACK_DREQ10:
		sp->i_state = NS_WCON_RREQ;
		break;
	case NS_WACK_DREQ11:
		sp->i_state = NS_WRES_RIND;
		break;
		/* 
		 *  Note: if we are not in a WACK state we simply do
		 *  not change state.  This occurs normally when we
		 *  send NOUTSTATE or NNOTSUPPORT or are responding to
		 *  an N_OPTMGMT_REQ in other than the NS_IDLE state.
		 */
	}
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_OK_ACK            19 - Success Acknowledgement
 *  ---------------------------------------------------------
 */
STATIC int n_ok_ack(queue_t * q, ulong prim, ulong seq, ulong tok)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_ok_ack_t *p;
	const size_t msg_len = sizeof(*p);

	(void) seq;
	(void) tok;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_OK_ACK;
	p->CORRECT_prim = prim;
	switch (sp->i_state) {
	case NS_WACK_OPTREQ:
		sp->i_state = NS_IDLE;
		break;
	case NS_WACK_RRES:
		sp->i_state = NS_DATA_XFER;
		break;
	case NS_WACK_UREQ:
		sp->i_state = NS_UNBND;
		break;
	case NS_WACK_DREQ6:
	case NS_WACK_DREQ9:
	case NS_WACK_DREQ10:
	case NS_WACK_DREQ11:
		sp->i_state = NS_IDLE;
		break;
		/* 
		 *  Note: if we are not in a WACK state we simply do
		 *  not change state.  This occurs normally when we
		 *  are responding to a N_OPTMGMT_REQ in other than
		 *  the NS_IDLE state.
		 */
	}
	putnext(sp->rq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_UNITDATA_IND      20 - Connection-less daa receive indication
 *  ---------------------------------------------------------------
 */
STATIC INLINE int n_unitdata_ind(queue_t * q, m3ua_addr_t * src, m3ua_addr_t * dst, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_unitdata_ind_t *p;
	size_t src_len = src ? sizeof(*src) + src->alen : 0;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t msg_len = sizeof(*p) + PAD4(src_len) + PAD4(dst_len);

	if ((1 << sp->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_UNITDATA_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->DEST_length = dst_len;;
	p->DEST_offset = dst_len ? sizeof(*p) + PAD4(src_len) : 0;
	p->ERROR_type = 0;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += PAD4(src_len);
	}
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += PAD4(dst_len);
	}
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_UDERROR_IND       21 - UNITDATA Error Indication
 *  ---------------------------------------------------------
 *  NOTE:- We might want to do a special N-NOTICE primitive here because the
 *  N_UDERROR_IND primitive does not have that much information about the
 *  returned message.  Or, if we attach the M_DATA block, the use might be
 *  able to gleen more information from the message itself.  Another approach
 *  is to stuff some extra information at the end of the primitive.
 */
STATIC INLINE int n_uderror_ind(queue_t * q, uint error, m3ua_addr_t * dst, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_uderror_ind_t *p;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t msg_len = sizeof(*p) + PAD4(dst_len);

	if ((1 << sp->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_UNITDATA_IND;
	p->DEST_length = dst_len;;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->RESERVED_field = 0;
	p->ERROR_type = error;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += PAD4(dst_len);
	}
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_DATACK_IND        24 - Data acknowledgement indication
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_datack_ind(queue_t * q)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_datack_ind_t *p;
	N_qos_sel_data_mtp_t *qos;
	const size_t qos_len = sizeof(*qos);
	const size_t msg_len = sizeof(*p) + qos_len;

	if ((1 << sp->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_DATACK_IND;
	qos = ((typeof(qos)) mp->b_wptr)++;
	qos->n_qos_type = N_QOS_SEL_DATA_MTP;
	qos->sls = sp->sls;	/* FIXME */
	qos->mp = sp->mp;	/* FIXME */
	fixme(("Get the SLS and MP from the arguments to this function\n"
	       "Actually, it may be worthwhile not delivering QOS\n"));
	mp->b_cont = dp;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_RESET_IND         26 - Incoming NC reset request indication
 *  -------------------------------------------------------------
 */
STATIC INLINE int n_reset_ind(queue_t * q, ulong orig, ulong reason, mblk_t * cp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_reset_ind_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << sp->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_RESET_IND;
	p->RESET_orig = orig;
	p->RESET_reason = reason;
	bufq_queue(&sp->conq, cp);
	sp->i_state = NS_WRES_RIND;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_RESET_CON         27 - Reset processing complete
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_reset_con(queue_t * q)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_reset_con_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << sp->i_state) & ~(NSF_WCON_RREQ))
		goto efault;
	if (!canputnext(sp->rq))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_RESET_CON;
	sp->i_state = NS_DATA_XFER;
	putnext(sp->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

#if 0
/* 
 *  N_RECOVER_IND       29 - NC Recovery indication
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_recover_ind(queue_t * q)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_recover_ind_t *p;
	const size_t msg_len = sizeof(*p);

	if (!canputnext(sp->q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_RECOVER_IND;
	sp->i_state = NS_DATA_XFER;
	putnext(sp->q, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_RETRIEVE_IND      32 - NC Retrieval indication
 *  ---------------------------------------------------------
 */
STATIC int n_retrieve_ind(queue_t * q, mblk_t * dp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_retrieve_ind_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << sp->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(sp->q))
		goto ebusy;
	if (!(mp = ua_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;

	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_RETREIVE_IND;
	mp->b_cont = dp;
	sp->i_state = NS_IDLE;
	putnext(sp->q, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: indication from wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_RETRIEVE_CON      33 - NC Retrieval complete confirmation
 *  -----------------------------------------------------------
 */
STATIC int n_retrieve_con(queue_t * q)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *mp;
	N_retrieve_con_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << sp->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(sp->q))
		goto ebusy;
	if ((mp = ua_allocb(q, msg_len, BPRI_MED)))
		mp->b_datap->db_type = M_PROTO;

	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = N_RETRIEVE_CON;
	sp->i_state = NS_IDLE;
	putnext(sp->q, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: confirmation from wrong state\n"));
	goto error;
      error:
	return (err);
}

#endif

/* 
 *  =========================================================================
 *
 *  STATE MACHINE Interface
 *
 *  =========================================================================
 */
STATIC int m3ua_conn_ind(m3ua_t * sp, mblk_t * cp)
{
	return n_conn_ind(sp->rq, cp);
}
STATIC int m3ua_conn_con(m3ua_t * sp, uint pcls, uint flags, m3ua_addr_t * res, mblk_t * dp)
{
	return n_conn_con(sp->rq, pcls, flags, res, dp);
}
STATIC int m3ua_data_ind(m3ua_t * sp, uint exp, uint more, mblk_t * dp)
{
	if (exp)
		return n_exdata_ind(sp->rq, more, dp);
	else
		return n_data_ind(sp->rq, more, dp);
}
STATIC int m3ua_datack_ind(m3ua_t * sp)
{
	return n_datack_ind(sp->rq);
}
STATIC int m3ua_reset_ind(m3ua_t * sp, ulong orig, ulong reason, mblk_t * cp)
{
	return n_reset_ind(sp->rq, orig, reason, cp);
}
STATIC int m3ua_reset_con(m3ua_t * sp)
{
	return n_reset_con(sp->rq);
}
STATIC int m3ua_discon_ind(m3ua_t * sp, ulong orig, long reason, m3ua_addr_t * res, mblk_t * cp,
			   mblk_t * dp)
{
	return n_discon_ind(sp->rq, orig, reason, res, cp, dp);
}
STATIC int m3ua_ordrel_ind(m3ua_t * sp)
{
	return m3ua_ordrel_req(sp->rq);
}
STATIC int
m3ua_unitdata_ind(m3ua_t * sp, m3ua_addr_t * dst, m3ua_addr_t * src, uint pcl,
		  uint opt, uint imp, uint seq, uint pri, mblk_t * dp)
{
	return n_unitdata_ind(sp->rq, src, dst, dp);
}
STATIC int
m3ua_uderror_ind(m3ua_t * sp, uint cause, m3ua_addr_t * dst, m3ua_addr_t * src,
		 uint pcl, uint opt, uint imp, uint seq, uint pri, mblk_t * dp)
{
	(void) src;
	return n_uderror_ind(sp->rq, cause, dst, dp);
}

STATIC struct m3ua_ifops m3ua_n_ops = {
	m3ua_conn_ind,
	m3ua_conn_con,
	m3ua_data_ind,
	m3ua_datack_ind,
	m3ua_reset_ind,
	m3ua_reset_con,
	m3ua_discon_ind,
	m3ua_ordrel_ind,
	m3ua_unitdata_ind,
	m3ua_uderror_ind
};

/* 
 *  =========================================================================
 *
 *  NPI User --> NPI Provider (M3UA) Primitives
 *
 *  =========================================================================
 */
/* 
 *  N_CONN_REQ       0 - NC request
 *  ---------------------------------------------------------------
 */
/* 
 *  4.2.1.1  N_CONN_REQ - Network Connection Request
 *
 *  This primtive requests that the NS provider make a network connection to
 *  the specified destination.  The format is one M_PROTO message block
 *  followed by one ore more M_DATA blocks for the NS user data transfer.  The
 *  specification of NS user data is optional.  The NS user can send any
 *  integral number of octets of data within the range supported by the NS
 *  provider (see N_INFO_ACK).  If the user does not specify QOS parameter
 *  values, the default values (specified via N_OPTMGMT_REQ) are used by the
 *  NS provider.
 *
 *  TODO: If a stream is bound as a listener stream [conind > 0], it will not
 *  be able to initiate connect requests.  If the NS user attempts to send an
 *  N_CONN_REQ primitive down this stream, an N_ERROR_ACK message will be sent
 *  to the NS user by the NS provider with an error value of NACCESS.
 */
STATIC int n_conn_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	m3ua_addr_t *a;
	int err = -EFAULT;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_conn_req_t *p = ((typeof(p)) mp->b_rptr);
	N_qos_sel_conn_mtp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset);

	if (sp->i_state != NS_IDLE)
		goto noutstate sp->i_state = NS_WCON_CREQ;
	if (mlen < sizeof(*p))
		goto einval;
	if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length)
		goto nbadopt2;
	if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_CONN_MTP)
		goto nbadqostype;
	if (p->QOS_length && p->QOS_length != sizeof(*qos))
		goto nbadopt;
	a = (m3ua_addr_t *) (mp->b_rptr + p->DEST_offset);
	if (!p->DEST_length)
		goto nnoaddr;
	if ((mlen < p->DEST_offset + p->DEST_length)
	    || (p->DEST_length < sizeof(m3ua_addr_t) + a->alen) || a->ssn)
		goto nbadaddr;
	if (sp->cred.cr_uid == 0 || a->ssn)
		goto naccess;
	fixme(("select a source local reference number\n"));
	if (p->QOS_length)
		sp->pclass = qos->protocol_class;
	sp->flags &= ~(M3UA_FLAG_REC_CONF_OPT | M3UA_FLAG_EX_DATA_OPT);
	if (p->CONN_flags & REC_CONF_OPT)
		sp->flags |= M3UA_FLAG_REC_CONF_OPT;
	if (p->CONN_flags & EX_DATA_OPT)
		sp->flags |= M3UA_FLAG_EX_DATA_OPT;
	if ((err = m3ua_conn_req(q, a, mp->b_cont)))
		goto badconn;

	mp->b_cont = NULL;	/* absorbed mp->b_cont */
	return (0);

      badconn:
	err = err;
	ptrace(("ERROR: couldn't connect\n"));
	goto error;
      naccess:
	err = NACCESS;
	ptrace(("ERROR: no permission for requested address\n"));
	goto error;
      nbadaddr:
	err = NBADADDR;
	ptrace(("ERROR: address is unusable\n"));
	goto error;
      nnoaddr:
	err = NNOADDR;
	ptrace(("ERROR: no destination address\n"));
	goto error;
      nbadopt:
	err = NBADOPT;
	ptrace(("ERROR: options are unusable\n"));
	goto error;
      nbadqostype:
	err = NBADQOSTYPE;
	ptrace(("ERROR: QOS structure type not supported\n"));
	goto error;
      nbadopt2:
	err = NBADOPT;
	ptrace(("ERROR: options are unusable\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid message format\n"));
	goto error;
      noutstate:
	err = NOUTSTATE;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      error:
	return n_error_ack(q, N_CONN_REQ, err);
}

/* 
 *  N_CONN_RES       1 - Accept previous connection indication
 *  ---------------------------------------------------------------
 *  We don't support connection indications or connection responses for
 *  connectionless protocols.
 */
STATIC int n_conn_res(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	return n_error_ack(q, N_CONN_RES, NNOTSUPPORT);
}

/* 
 *  N_DISCON_REQ         2 - NC disconnection request
 *  ---------------------------------------------------------
 */
STATIC int n_discon_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	mblk_t *cp = NULL;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_discon_req_t *p = (typeof(p)) mp->b_rptr;

	switch (sp->i_state) {
	case NS_WCON_CREQ:
		sp->i_state = NS_WACK_DREQ6;
		break;
	case NS_DATA_XFER:
		sp->i_state = NS_WACK_DREQ9;
		break;
	case NS_WCON_RREQ:
		sp->i_state = NS_WACK_DREQ10;
		break;
	case NS_WRES_RIND:
		sp->i_state = NS_WACK_DREQ11;
		break;
	default:
		goto noutstate;
	}
	if (mlen < sizeof(*p))
		goto einval;
	if (p->RES_length)
		goto nbadaddr;
	if (p->SEQ_number)
		goto nbadseq;
	/* 
	 *  XXX: What to do with the disconnect reason?  Nothing?
	 */
	if ((err = m3ua_discon_req(q, cp)))
		goto baddisconn;
	return n_ok_ack(q, N_DISCON_REQ, 0, 0);
      baddiscon:
	err = err;
	ptrace(("ERROR: couldn't disconnect\n"));
	goto error;
      nbadseq:
	err = NBADSEQ;
	ptrace(("ERROR: connection ind reference is invalid\n"));
	goto error;
      nbadaddr:
	err = NBADADDR;
	ptrace(("ERROR: responding addres invalid\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      noutstate:
	err = NOUTSTATE;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      error:
	return n_error_ack(q, N_DISCON_REQ, err);
}

/* 
 *  N_DATA_REQ           3 - Connection-Mode data transfer request
 *  --------------------------------------------------------------
 */
STATIC int n_error_reply(queue_t * q, int err)
{
	m3ua_t *sp = M3UA_PRIV(q);
	mblk_t *mp;

	switch (err) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
	case 0:
	case 1:
	case 2:
		ptrace(("ERROR: No error condition\n"));
		return (err);
	}
	if ((mp = ua_allocb(q, 2, BPRI_HI))) {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		*(mp->b_wptr)++ = err < 0 ? -err : err;
		putnext(sp->rq, mp);
		return (0);
	}
	return (-ENOBUFS);
}

STATIC INLINE int n_write(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;

	if (sp->i_state == NS_IDLE)
		goto discard;
	if ((1 << sp->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND))
		goto eproto;
	if ((err = m3ua_data_req(q, 0, 0, 0, mp)))
		goto error;
	return (QR_ABSORBED);	/* absorbed mp */
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      discard:
	err = 0;
	ptrace(("ERROR: ignoring in idle state\n"));
	return (err);
      error:
	return n_error_reply(q, err);
}

STATIC INLINE int n_data_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	N_qos_sel_data_mtp_t *qos = NULL;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_data_req_t *p = (typeof(p)) mp->b_rptr;
	uint exp, more, rcpt;

	if (sp->i_state == NS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen >= sizeof(*p) + sizeof(*qos)) {
		qos = (typeof(qos)) (p + 1);
		if (qos->n_qos_type != N_QOS_SEL_DATA_MTP)
			qos = NULL;
	}
	if ((1 << sp->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND))
		goto eproto;
	exp = 0;		/* FIXME */
	more = 0;		/* FIXME */
	rcpt = 0;		/* FIXME */
	fixme(("We need to adjust the data req function to take sls and mp\n"));
	if ((err = m3ua_data_req(q, exp, more, rcpt, mp->b_cont)))
		goto error;
	return (QR_ABSORBED);	/* absorbed mp->b_cont */
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	ptrace(("ERROR: ignore in idle state\n"));
	return (0);
      error:
	return n_error_reply(q, err);
}

/* 
 *  N_EXDATA_REQ         4 - Expedited data request
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_exdata_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	N_qos_sel_data_mtp_t *qos = NULL;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_exdata_req_t *p = (typeof(p)) mp->b_rptr;
	uint exp, more, rcpt;

	if (sp->i_state == NS_IDLE)
		goto discard;
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen >= sizeof(*p) + sizeof(*qos)) {
		qos = (typeof(qos)) (p + 1);
		if (qos->n_qos_type != N_QOS_SEL_DATA_MTP)
			qos = NULL;
	}
	if ((1 << sp->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND))
		goto eproto;
	exp = 0;		/* FIXME */
	more = 0;		/* FIXME */
	rcpt = 0;		/* FIXME */
	fixme(("We need to adjust the data req function to take sls and mp\n"));
	if ((err = m3ua_data_req(q, exp, more, rcpt, mp->b_cont)))
		goto error;
	return (QR_ABSORBED);	/* absorbed mp->b_cont */
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      discard:
	ptrace(("ERROR: ignore in idle state\n"));
	return (0);
      error:
	return n_error_reply(q, err);
}

/* 
 *  N_INFO_REQ           5 - Information request
 *  ---------------------------------------------------------
 */
STATIC int n_info_req(queue_t * q, mblk_t * mp)
{
	(void) mp;
	return n_info_ack(q);
}

/* 
 *  N_BIND_REQ           6 - Bind a NS user to network address
 *  ----------------------------------------------------------
 */
STATIC int n_bind_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_bind_req_t *p = (typeof(p)) mp->b_rptr;
	m3ua_addr_t *a;

	if (sp->i_state != NS_UNBND)
		goto noutstate;
	sp->i_state = NS_WACK_BREQ;
	if (mlen < sizeof(*p))
		goto einval;
	a = (m3ua_addr_t *) (mp->b_rptr + p->ADDR_offset);
	if ((mlen < p->ADDR_offset + p->ADDR_length)
	    || (p->ADDR_length < sizeof(m3ua_addr_t) + a->alen) && a->ssn)
		goto nbadaddr;
	fixme(("Deal with wildcards\n"));
	fixme(("Deal with default binds\n"));
	fixme(("Deal with slr assignment\n"));
	if (0)
		goto nnoaddr;
	if (sp->cred.cr_uid != 0 && !a->ssn)
		goto naccess;
	if ((err = m3ua_bind_req(q, a, p->CONIND_number)))
		goto error;
	return n_bind_ack(q);
      naccess:
	err = NACCESS;
	ptrace(("ERROR: no permission for requested address\n"));
	goto error;
      nnoaddr:
	err = NNOADDR;
	ptrace(("ERROR: could not allocate address\n"));
	goto error;
      nbadaddr:
	err = NBADADDR;
	ptrace(("ERROR: address is invalid\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: invalid primitive format\n"));
	goto error;
      noutstate:
	err = NOUTSTATE;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      error:
	return n_error_ack(q, N_BIND_REQ, err);
}

/* 
 *  N_UNBIND_REQ         7 - Unbind NS user from network address
 *  ------------------------------------------------------------
 */
STATIC int n_unbind_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	N_unbind_req_t *p = (typeof(p)) mp->b_rptr;

	(void) p;
	if (sp->i_state != NS_IDLE)
		goto noutstate;
	sp->i_state = NS_WACK_UREQ;
	if ((err = m3ua_unbind_req(q)))
		goto error;
	return n_ok_ack(q, N_UNBIND_REQ, 0, 0);
      noutstate:
	err = NOUTSTATE;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      error:
	return n_error_ack(q, N_UNBIND_REQ, err);
}

/* 
 *  N_UNITDATA_REQ       8 - Unitdata request
 *  ---------------------------------------------------------
 */
STATIC int n_uderror_reply(queue_t * q, mblk_t * mp, int etype)
{
	N_uderror_ind_t *p = (typeof(p)) mp->b_rptr;

	switch (etype) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (etype);
	case 0:
	case 1:
	case 2:
		never();
		return (etype);
	}
	if (etype > 0) {
		p->PRIM_type = N_UDERROR_IND;
		p->ERROR_type = etype;
	} else {
		mp->b_datap->db_type = M_ERROR;
		mp->b_wptr = mp->b_rptr;
		*(mp->b_wptr)++ = -etype;
		*(mp->b_wptr)++ = -etype;
	}
	qreply(q, mp);
	return (QR_ABSORBED);
}

STATIC INLINE int n_unitdata_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	const N_unitdata_req_t *p = (typeof(p)) mp->b_rptr;
	N_qos_sel_data_mtp_t *qos;
	m3ua_addr_t *dst;
	ulong sls, mp;

	if (dlen == 0 || dlen > sp->mtu)
		goto eproto;
	if (sp->i_state != NS_IDLE)
		goto eproto2;
	if (mlen < sizeof(*p))
		goto eproto3;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	qos = NULL;
	if ((mlen < p->DEST_offset + p->DEST_length)
	    || (!p->DEST_offset) || (p->DEST_offset < sizeof(*p))
	    || (p->DEST_length < sizeof(*dst)) || (p->DEST_length < sizeof(*dst) + dst->alen))
		goto eproto4;
	if (sp->cred.cr_uid != 0 && !dst->ssn)
		goto eproto5;
	if (mlen >= p->DEST_offset + p->DEST_length + PAD4(dst->alen) + sizeof(*qos)) {
		qos = (typeof(qos)) (mp->b_rptr + p->DEST_offset + p->DEST_length);
		if (qos->n_qos_type != N_QOS_SEL_DATA_MTP)
			qos = NULL;
	}
	sls = qos ? qos->sls : sp->sls;
	mp = qos ? qos->mp : sp->mp;
	fixme(("We need to adjust the data req function to take sls and mp\n"));
	if ((err = m3ua_unitdata_req(q, dst, pclass, options, import, seq, pri, mp->b_cont)))
		goto error;
	return (QR_TRIMMED);
      eproto5:
	err = -EPROTO;
	ptrace(("ERROR: no permission to send to address\n"));
	goto error;
      eproto4:
	err = -EPROTO;
	ptrace(("ERROR: bad destnation address\n"));
	goto error;
      eproto3:
	err = -EPROTO;
	ptrace(("ERROR: invalid primitive\n"));
	goto error;
      eproto2:
	err = -EPROTO;
	ptrace(("ERROR: would place i/f out of state\n"));
	goto error;
      eproto:
	err = -EPROTO;
	ptrace(("ERROR: bad data size\n"));
	goto error;
      error:
	return n_uderror_reply(q, mp, err);
}

/* 
 *  N_OPTMGMT_REQ        9 - Options management request
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_optmgmt_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_optmgmt_req_t *p = (typeof(p)) mp->b_rptr;
	N_qos_sel_info_mtp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset);

	if (sp->i_state == NS_IDLE)
		sp->i_state = NS_WACK_OPTREQ;
	if (mlen < sizeof(*p))
		goto einval;
	if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length)
		goto nbadopt;
	if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_INFO_MTP)
		goto nbadqostype;
	if (p->QOS_length && p->QOS_length != sizeof(*qos))
		goto nbadopt2;
	if (p->QOS_length) {
		if (qos->protocol_class != -1UL)
			sp->pvar = qos->pvar;
		if (qos->option_flags != -1UL)
			sp->popt = qos->popt;
	}
	if (p->OPTMGMT_flags & DEFAULT_RC_SEL)
		sp->flags |= M3UA_FLAG_DEFAULT_RC_SEL;
	else
		sp->flags &= ~M3UA_FLAG_DEFAULT_RC_SEL;
	return n_ok_ack(q, N_OPTMGMT_REQ, 0, 0);
      nbadopt2:
	err = NBADOPT;
	ptrace(("ERROR: invalid qos options\n"));
	goto error;
      nbadqostype:
	err = NBADQOSTYPE;
	ptrace(("ERROR: bad qos structure type\n"));
	goto error;
      nbadopt:
	err = NBADOPT;
	ptrace(("ERROR: bad qos structure format\n"));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("ERROR: bad primitive format\n"));
	goto error;
      error:
	return n_error_ack(q, N_OPTMGMT_REQ, err);
}

/* 
 *  N_DATACK_REQ        23 - Data acknowledgement request
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_datack_req(queue_t * q, mblk_t * mp)
{
	(void) mp;
	fixme(("SWERR: Write this function\n"));
	return n_error_ack(q, N_DATACK_REQ, NNOTSUPPORT);
}

/* 
 *  N_RESET_REQ         25 - NC reset request
 *  ---------------------------------------------------------
 */
STATIC int n_reset_req(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_reset_req_t *p = (typeof(p)) mp->b_rptr;

	if (sp->i_state == NS_IDLE)
		goto discard;
	if (sp->i_state != NS_DATA_XFER)
		goto noutstate;
	sp->i_state = NS_WCON_RREQ;
	(void) p;
	(void) mlen;
	if ((err = m3ua_reset_req(q)))
		goto error;
	return (0);
      noutstate:
	err = NOUTSTATE;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      discard:
	ptrace(("ERROR: ignore in idle state\n"));
	return (0);
      error:
	return n_error_ack(q, N_RESET_REQ, err);
}

/* 
 *  N_RESET_RES         27 - Reset processing accepted
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_reset_res(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_reset_res_t *p = (typeof(p)) mp->b_rptr;

	if (sp->i_state == NS_IDLE)
		goto discard;
	if (sp->i_state != NS_WRES_RIND)
		goto noutstate;
	sp->i_state = NS_WACK_RRES;
	(void) p;
	(void) mlen;
	if ((err = m3ua_reset_res(q)))
		goto error;
	return n_ok_ack(q, N_RESET_RES, 0, 0);
      noutstate:
	err = NOUTSTATE;
	ptrace(("ERROR: would place interface out of state\n"));
	goto error;
      discard:
	ptrace(("ERROR: ignore in idle state\n"));
	return (0);
      error:
	return n_error_ack(q, N_RESET_RES, err);
}

STATIC int n_other_req(queue_t * q, mblk_t * mp)
{
	ulong prim = *((ulong *) mp->b_rptr);

	return n_error_ack(q, prim, NNOTSUPPORT);
}

/* 
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m3ua_n_w_ioctl(queue_t * q, mblk_t * mp)
{
	int ret = -EINVAL;

	// void *arg = mp->b_cont?mp->b_cont->b_rptr:NULL;
	struct iocblk *iocp = (typeof(iocp)) mp->b_wptr;
	int cmd = iocp->ioc_cmd;

	switch (_IOC_TYPE(cmd) >> 8) {
	case __SID:
		ret = -EOPNOTSUPP;
		break;
	case M3UA_IOC_MAGIC:
		if (iocp->ioc_count >= _IOC_SIZE(cmd)) {
			int nr = _IOC_NR(cmd);

			switch (nr) {
			default:
				ret = -EOPNOTSUPP;
				break;
			}
		}
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	mp->b_datap->db_type = ret < 0 ? M_IOCNAK : M_IOCACK;
	iocp->ioc_error = ret < 0 ? -ret : 0;
	iocp->ioc_rval = ret < 0 ? -1 : ret;
	qreply(q, mp);
	return (0);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m3ua_n_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	m3ua_t *sp = M3UA_PRIV(q);
	ulong oldstate = sp->i_state;

	switch ((prim = *((ulong *) mp->b_rptr))) {
	case N_CONN_REQ:
		rtn = n_conn_req(q, mp);
		break;
	case N_CONN_RES:
		rtn = n_conn_res(q, mp);
		break;
	case N_DISCON_REQ:
		rtn = n_discon_req(q, mp);
		break;
	case N_DATA_REQ:
		rtn = n_data_req(q, mp);
		break;
	case N_EXDATA_REQ:
		rtn = n_exdata_req(q, mp);
		break;
	case N_INFO_REQ:
		rtn = n_info_req(q, mp);
		break;
	case N_BIND_REQ:
		rtn = n_bind_req(q, mp);
		break;
	case N_UNBIND_REQ:
		rtn = n_unbind_req(q, mp);
		break;
	case N_OPTMGMT_REQ:
		rtn = n_optmgmt_req(q, mp);
		break;
	case N_RESET_REQ:
		rtn = n_reset_req(q, mp);
		break;
	case N_RESET_RES:
		rtn = n_reset_res(q, mp);
		break;
	case N_UNITDATA_REQ:
		rtn = n_unitdata_req(q, mp);
		break;
	default:
		rtn = n_other_req(q, mp);
		break;
	}
	if (rtn < 0) {
		seldom();
		sp->i_state = oldstate;
	}
	return (rtn);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m3ua_n_w_data(queue_t * q, mblk_t * mp)
{
	(void) mp;
	return n_write(q, mp);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Other messages
 *
 *  -------------------------------------------------------------------------
 */
STATIC int m3ua_n_w_other(queue_t * q, mblk_t * mp)
{
	m3ua_t *sp = M3UA_PRIV(q);

	cmn_err(CE_WARN, "Unsupported lock type %d on WR(q) %d\n", mp->b_datap->db_type,
		getminor(sp->devid));
	return (-EOPNOTSUPP);
}

/* 
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  WRITE PUT and SERVICE (Message from above M3UA-User --> M3UA-Provider)
 *  -------------------------------------------------------------------------
 */
STATIC int m3ua_n_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return m3ua_n_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return m3ua_n_w_proto(q, mp);
	case M_FLUSH:
		return m3ua_w_flush(q, mp);
	case M_IOCTL:
		return m3ua_n_w_ioctl(q, mp);
	default:
		return m3ua_n_w_other(q, mp);
	}
	return (-EOPNOTSUPP);
}

/* 
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  -------------------------------------------------------------------------
 */
int m3ua_n_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	m3ua_t *sp;
	priv_t *pp, **ppp;
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);

	if (sflag == MODOPEN || WR(q)->q_next) {
		ptrace(("ERROR: Can't push as module\n"));
		return (EIO);
	}
	if (M3UA_PRIV(q) != NULL) {
		ptrace(("INFO: Device already open\n"));
		return (0);
	}
	if (sflag == CLONEOPEN) {
		ptrace(("INFO: Clone open in effect\n"));
		cmajor = M3UA_CMAJOR;
		cminor = 0;
	}
	if (cmajor == M3UA_CMAJOR && cminor == 0 && sflag != CLONEOPEN) {
		ptrace(("INFO: Clone minor opened\n"));
		sflag = CLONEOPEN;
		cminor = 1;
	}
	for (ppp = &m3ua_opens; *ppp; ppp = &(*ppp)->next) {
		ushort dmajor = getmajor((*ppp)->u.devid);

		if (cmajor < dmajor)
			break;
		if (cmajor == dmajor) {
			ushort dminor = getminor((*ppp)->u.devid);

			if (cminor < dminor)
				break;
			if (cminor == dminor) {
				if (sflag == CLONEOPEN) {
					if (++cminor >= M3UA_NMINOR) {
						if (++cmajor >= M3UA_CMAJOR + M3UA_NMAJOR)
							break;
						cminor = 0;
					}
					continue;
				}
				ptrace(("ERROR: Requested device (%d,%d) in use\n", cmajor,
					cminor));
				return (EIO);
			}
		}
	}
	if (cmajor >= M3UA_CMAJOR + M3UA_NMINOR) {
		ptrace(("ERROR: No devices available\n"));
		return (ENXIO);
	}
	if (!((void *) sp = (void *) pp = kmalloc(sizeof(*sp), GFP_KERNEL))) {
		ptrace(("ERROR: Could not allocate private structure\n"));
		return (ENOMEM);
	}
	*devp = makedevice(cmajor, cminor);
	ptrace(("INFO: Making device major %d, minor %d\n", cmajor, cminor));
	bzero(sp, sizeof(*sp));
	M3UA_PRIV(q) = M3UA_PRIV(WR(q)) = sp;
	sp->rq = q;
	sp->wq = WR(q);
	sp->devid = *devp;
	sp->ops.w_prim = &m3ua_n_w_prim;
	sp->ops.r_prim = &m3ua_u_r_prim;
	sp->ifops = &m3ua_n_ops;
	if ((pp->next = *ppp))
		pp->next->prev = &pp->next;
	pp->prev = ppp;
	*ppp = pp;
	return (0);

}

/* 
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
int m3ua_n_close(queue_t * q, int flag, cred_t * crp)
{
	m3ua_t *sp = M3UA_PRIV(q);

	if (!sp)
		return (EIO);
	if (sp->mtp) {
		/* 
		 *  Detach this M3UA stream from whatever SP streams it is
		 *  using on the lower half.
		 */
		mtp_t *mtp = sp->mtp;

		if ((--sp->mtp->use_count) <= 0) {
#if 0				/* doesn't really apply to mtp's */
			/* 
			 *  Unbind the SP stream if it is not being used by
			 *  any M3UA streams.
			 */
			mblk_t *mp;
			N_unbind_req_t *p;

			if (!(mp = allocb(sizeof(*p), BPRI_MED)))
				return (ENOSR);
			mp->b_datap->db_type = M_PCPROTO;
			p = (N_unbind_req_t *) mp->b_wptr;
			p->PRIM_type = N_UNBIND_REQ;
			mp->b_wptr += sizeof(*p);
			mtp->state = NS_WACK_UREQ;
			putnext(WR(mtp->q), mp);
#endif
			mtp->use_count = 0;
		}
	}
	/* 
	 *  TODO:  Might want to do some more things to deallocate some M3UA
	 *  structures associated with the module.  I.e. some M3UA bindings.
	 */
	if (sp->rbid)
		unbufcall(xchg(&sp->rbid, 0));
	if (sp->wbid)
		unbufcall(xchg(&sp->wbid, 0));
	if ((*(sp->prev) = sp->next))
		sp->next->prev = sp->prev;
	M3UA_PRIV(WR(q)) = M3UA_PRIV(RD(q)) = NULL;
	kfree(sp);
	return (0);
}

/* 
 *  =========================================================================
 *
 *  Linux Kernel Module Initialization
 *
 *  =========================================================================
 */
int init_module(void)
{
	cmn_err(CE_NOTE, M3UA_BANNER);	/* console splash */
	return (0);
}

void cleanup_module(void)
{
	return;
}


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sigtran/m3ua_n.c

OpenSS7
SS7 for the
Common Man
Home Overview Status News Documentation Resources About

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified: