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/sua_n.c


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



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

static char const ident[] =
    "$RCSfile: sua_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 "sua_n.h"

#define SUA_DESCRIP	"SUA STREAMS MULTIPLEXING DRIVER -- NPI INTERFACE." "\n" \
			"Part of the OpenSS7 Stack for LiS STREAMS."
#define SUA_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corp.  All Rights Reserved"
#define SUA_DEVICE	"Supports OpenSS7 MTP Level 3 Drivers."
#define SUA_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define SUA_LICENSE	"GPL"
#define SUA_BANNER	SUA_DESCRIP	"\n" \
			SUA_COPYRIGHT	"\n" \
			SUA_DEVICE	"\n" \
			SUA_CONTACT	"\n"

MODULE_AUTHOR(SUA_CONTACT);
MODULE_DESCRIPTION(SUA_DESCRIP);
MODULE_SUPPORTED_DEVICE(SUA_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(SUA_LICENSE);
#endif

/* 
 *  =========================================================================
 *
 *  NPI Provider (SUA) -> NPI User Primitives
 *
 *  =========================================================================
 */
/* 
 *  N_CONN_IND          11 - Incoming connection indication
 *  -------------------------------------------------------------
 */
STATIC INLINE int n_conn_ind(queue_t * q, mblk_t * cp)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_conn_ind_t *p;
	N_qos_sel_conn_sccp_t *qos;
	sua_addr_t *dst, *src;
	struct sua_cr *m = (struct sua_cr *) cp->b_rptr;
	size_t dst_len = sizeof(priv->dst) + priv->dst.alen;
	size_t src_len = sizeof(priv->src) + priv->src.alen;
	size_t qos_len = sizeof(*qos);
	size_t msg_len = sizeof(*p) + dst_len + src_len + qos_len;

	if ((1 << priv->i_state) & ~(NSF_IDLE | NSF_WRES_CIND))
		goto efault;
	if (bufq_length(&priv->conq) >= priv->conind)
		goto erestart;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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_CONN_IND;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) + dst_len : 0;
	p->SEQ_number = (ulong) cp;
	p->CONN_flags = REC_CONF_OPT | EX_DATA_OPT;
	p->QOS_length = qos_len;
	p->QOS_offset = qos_len ? sizeof(*p) + dst_len + src_len : 0;
	if (dst_len) {
		(void) m;	/* FIXME */
		dst = (typeof(dst)) mp->b_wptr;
		bcopy(dst, &priv->dst, dst_len);
		mp->b_wptr += dst_len;
	}
	if (src_len) {
		src = (typeof(src)) mp->b_wptr;
		bcopy(src, &priv->src, src_len);
		mp->b_wptr += src_len;
	}
	if (qos_len) {
		qos = ((typeof(qos)) mp->b_wptr)++;
		qos->n_qos_type = N_QOS_SEL_CONN_SCCP;
		qos->protocol_class = priv->pclass;
	}
	bufq_queue(&priv->conq, cp);
	priv->i_state = NS_WRES_CIND;
	putnext(priv->rq, mp);
	return (0);

      enobufs:
	err = -ENOBUFS;
	ptrace(("ERROR: No buffers\n"));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("ERROR: Flow controlled\n"));
	goto error;
      erestart:
	err = -ERESTART;
	ptrace(("ERROR: Too many connect inds\n"));
	goto error;
      efault:
	err = -EFAULT;
	ptrace(("SWERR: Indication in wrong state\n"));
	goto error;
      error:
	return (err);
}

/* 
 *  N_CONN_CON          12 - Connection established
 *  ---------------------------------------------------------
 */
STATIC INLINE int n_conn_con(queue_t * q, uint pcls, uint flags, sua_addr_t * res, mblk_t * dp)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_conn_con_t *p;
	N_qos_sel_conn_sccp_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 ((priv->i_state != NS_WCON_CREQ))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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_SCCP;
		qos->protocol_class = pcls;
	}
	mp->b_cont = dp;
	priv->i_state = NS_DATA_XFER;
	putnext(priv->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 SUA 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, sua_addr_t * res, mblk_t * seq, mblk_t * dp)
{
	sua_t *priv = SUA_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 << priv->i_state) & ~(NSF_WCON_CREQ | NSF_WRES_CIND
				     | NSF_DATA_XFER | NSF_WCON_RREQ | NSF_WRES_RIND))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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(&priv->conq, seq);
		freemsg(seq);
	}
	if (!bufq_length(&priv->conq))
		priv->i_state = NS_IDLE;
	else
		priv->i_state = NS_WRES_CIND;
	mp->b_cont = dp;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_data_ind_t *p;
	N_qos_sel_data_sccp_t *qos;
	const size_t qos_len = sizeof(*qos);
	const size_t msg_len = sizeof(*p) + qos_len;

	if ((1 << priv->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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_SCCP;
	qos->protocol_class = priv->pclass;	/* FIXME */
	qos->option_flags = priv->options;	/* FIXME */
	qos->importance = priv->imp;	/* FIXME */
	qos->sequence_selection = priv->sls;	/* FIXME */
	qos->message_priority = priv->mp;	/* FIXME */
	mp->b_cont = dp;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_exdata_ind_t *p;
	N_qos_sel_data_sccp_t *qos;
	const size_t qos_len = sizeof(*qos);
	const size_t msg_len = sizeof(*p) + qos_len;

	if ((1 < priv->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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_SCCP;
	qos->protocol_class = priv->pclass;	/* FIXME */
	qos->option_flags = priv->options;	/* FIXME */
	qos->importance = priv->imp;	/* FIXME */
	qos->sequence_selection = priv->sls;	/* FIXME */
	qos->message_priority = priv->mp;	/* FIXME */
	mp->b_cont = dp;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_info_ack_t *p;
	N_qos_sel_info_sccp_t *qos;
	N_qos_range_info_sccp_t *qor;
	sua_addr_t *add, *ss = &priv->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 = sua_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(sua_addr_t) + SUA_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 | ((priv->flags & SUA_FLAG_DEFAULT_RC_SEL) ? DEFAULT_RC_SEL : 0);
	p->NIDU_size = -1;
	p->SERV_type = N_CONS | N_CLNS;
	p->CURRENT_state = priv->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_SCCP;
		qos->protocol_class = priv->pclass;
		qos->option_flags = priv->options;
	}
	if (qor_len) {
		qor = ((typeof(qor)) mp->b_wptr)++;
		qor->n_qos_type = N_QOS_RANGE_INFO_SCCP;
		qor->protocol_classes = priv->pclass;
		qor->sequence_selection = priv->sls;
	}
	if (pro_len) {
		pro = ((typeof(pro)) mp->b_wptr)++;
		*pro = 3;
	}
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_bind_ack_t *p;
	sua_addr_t *add, *ss = &priv->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 = sua_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 = priv->conind;
	p->TOKEN_value = (ulong) priv->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;
	}
	priv->i_state = NS_IDLE;
	putnext(priv->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)
{
	sua_t *priv = SUA_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 = sua_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 (priv->i_state) {
	case NS_WACK_OPTREQ:
	case NS_WACK_UREQ:
	case NS_WCON_CREQ:
		priv->i_state = NS_IDLE;
		break;
	case NS_WCON_RREQ:
		priv->i_state = NS_DATA_XFER;
		break;
	case NS_WACK_BREQ:
		priv->i_state = NS_UNBND;
		break;
	case NS_WACK_CRES:
		priv->i_state = NS_WRES_CIND;
		break;
	case NS_WACK_DREQ6:
		priv->i_state = NS_WCON_CREQ;
		break;
	case NS_WACK_DREQ7:
		priv->i_state = NS_WRES_CIND;
		break;
	case NS_WACK_DREQ9:
		priv->i_state = NS_DATA_XFER;
		break;
	case NS_WACK_DREQ10:
		priv->i_state = NS_WCON_RREQ;
		break;
	case NS_WACK_DREQ11:
		priv->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(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_ok_ack_t *p;
	const size_t msg_len = sizeof(*p);

	if (!(mp = sua_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 (priv->i_state) {
	case NS_WACK_OPTREQ:
		priv->i_state = NS_IDLE;
		break;
	case NS_WACK_RRES:
		priv->i_state = NS_DATA_XFER;
		break;
	case NS_WACK_UREQ:
		priv->i_state = NS_UNBND;
		break;
	case NS_WACK_CRES:
	{
		queue_t *aq = (queue_t *) tok;
		sua_t *ap = SUA_PRIV(aq);

		if (ap) {
			ap->i_state = NS_DATA_XFER;
			sua_cleanup_read(q);	/* deliver to user what is possible */
			sua_transmit_wakeup(q);	/* reply to peer what is necessary */
		}
		if (seq) {
			bufq_unlink(&priv->conq, (mblk_t *) seq);
			freemsg((mblk_t *) seq);
		}
		if (aq != priv->rq) {
			if (bufq_length(&priv->conq))
				priv->i_state = NS_WRES_CIND;
			else
				priv->i_state = NS_IDLE;
		}
		break;
	}
	case NS_WACK_DREQ7:
		if (seq)
			bufq_unlink(&priv->conq, (mblk_t *) seq);
	case NS_WACK_DREQ6:
	case NS_WACK_DREQ9:
	case NS_WACK_DREQ10:
	case NS_WACK_DREQ11:
		if (bufq_length(&priv->conq))
			priv->i_state = NS_WRES_CIND;
		else
			priv->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(priv->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, sua_addr_t * src, sua_addr_t * dst, mblk_t * dp)
{
	sua_t *priv = SUA_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 << priv->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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(priv->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, sua_addr_t * dst, mblk_t * dp)
{
	sua_t *priv = SUA_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 << priv->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_datack_ind_t *p;
	N_qos_sel_data_sccp_t *qos;
	const size_t qos_len = sizeof(*qos);
	const size_t msg_len = sizeof(*p) + qos_len;

	if ((1 << priv->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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_SCCP;
	qos->protocol_class = priv->pclass;	/* FIXME */
	qos->option_flags = priv->options;	/* FIXME */
	qos->importance = priv->imp;	/* FIXME */
	qos->sequence_selection = priv->sls;	/* FIXME */
	qos->message_priority = priv->mp;	/* FIXME */
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_reset_ind_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << priv->i_state) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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(&priv->conq, cp);
	priv->i_state = NS_WRES_RIND;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_reset_con_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << priv->i_state) & ~(NSF_WCON_RREQ))
		goto efault;
	if (!canputnext(priv->rq))
		goto ebusy;
	if (!(mp = sua_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;
	priv->i_state = NS_DATA_XFER;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_recover_ind_t *p;
	const size_t msg_len = sizeof(*p);

	if (!canputnext(priv->q))
		goto ebusy;
	if (!(mp = sua_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;
	priv->i_state = NS_DATA_XFER;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_retrieve_ind_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << priv->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(priv->q))
		goto ebusy;
	if (!(mp = sua_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;
	priv->i_state = NS_IDLE;
	putnext(priv->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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *mp;
	N_retrieve_con_t *p;
	const size_t msg_len = sizeof(*p);

	if ((1 << priv->i_state) & ~(NSF_IDLE))
		goto efault;
	if (!canputnext(priv->q))
		goto ebusy;
	if ((mp = sua_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;
	priv->i_state = NS_IDLE;
	putnext(priv->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 sua_conn_ind(sua_t * priv, mblk_t * cp)
{
	return n_conn_ind(priv->rq, cp);
}
STATIC int sua_conn_con(sua_t * priv, uint pcls, uint flags, sua_addr_t * res, mblk_t * dp)
{
	return n_conn_con(priv->rq, pcls, flags, res, dp);
}
STATIC int sua_data_ind(sua_t * priv, uint exp, uint more, mblk_t * dp)
{
	if (exp)
		return n_exdata_ind(priv->rq, more, dp);
	else
		return n_data_ind(priv->rq, more, dp);
}
STATIC int sua_datack_ind(sua_t * priv)
{
	return n_datack_ind(priv->rq);
}
STATIC int sua_reset_ind(sua_t * priv, ulong orig, ulong reason, mblk_t * cp)
{
	return n_reset_ind(priv->rq, orig, reason, cp);
}
STATIC int sua_reset_con(sua_t * priv)
{
	return n_reset_con(priv->rq);
}
STATIC int sua_discon_ind(sua_t * priv, ulong orig, long reason, sua_addr_t * res, mblk_t * cp,
			  mblk_t * dp)
{
	return n_discon_ind(priv->rq, orig, reason, res, cp, dp);
}
STATIC int sua_ordrel_ind(sua_t * priv)
{
	return sua_ordrel_req(priv->rq);
}
STATIC int
sua_unitdata_ind(sua_t * priv, sua_addr_t * dst, sua_addr_t * src, uint pcl,
		 uint opt, uint imp, uint seq, uint pri, mblk_t * dp)
{
	return n_unitdata_ind(priv->rq, src, dst, dp);
}
STATIC int
sua_uderror_ind(sua_t * priv, uint cause, sua_addr_t * dst, sua_addr_t * src,
		uint pcl, uint opt, uint imp, uint seq, uint pri, mblk_t * dp)
{
	(void) src;
	return n_uderror_ind(priv->rq, cause, dst, dp);
}

STATIC struct sua_ifops sua_n_ops = {
	sua_conn_ind,
	sua_conn_con,
	sua_data_ind,
	sua_datack_ind,
	sua_reset_ind,
	sua_reset_con,
	sua_discon_ind,
	sua_ordrel_ind,
	sua_unitdata_ind,
	sua_uderror_ind
};

/* 
 *  =========================================================================
 *
 *  NPI User --> NPI Provider (SUA) 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)
{
	sua_t *priv = SUA_PRIV(q);
	sua_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_sccp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset);

	if (priv->i_state != NS_IDLE)
		goto noutstate priv->i_state = NS_WCON_CREQ;
	if (mlen < sizeof(*p))
		goto einval;
	if (p->QOS_length && mlen < p->QOS_offset + p->QOS_length)
		goto nbadopt3;
	if (p->QOS_length && qos->n_qos_type != N_QOS_SEL_CONN_SCCP)
		goto nbadqostype;
	if (p->QOS_length && qos->protocol_class > 3)
		goto nbadopt2;
	if (p->QOS_length && qos->protocol_class != 2 && qos->protocol_class != 3)
		goto nnotsupport;
	if (p->QOS_length && p->QOS_length != sizeof(*qos))
		goto nbadopt;
	a = (sua_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(sua_addr_t) + a->alen) || a->ssn)
		goto nbadaddr;
	if (priv->cred.cr_uid == 0 || a->ssn)
		goto naccess;
	fixme(("select a source local reference number\n"));
	if (p->QOS_length)
		priv->pclass = qos->protocol_class;
	priv->flags &= ~(SUA_FLAG_REC_CONF_OPT | SUA_FLAG_EX_DATA_OPT);
	if (p->CONN_flags & REC_CONF_OPT)
		priv->flags |= SUA_FLAG_REC_CONF_OPT;
	if (p->CONN_flags & EX_DATA_OPT)
		priv->flags |= SUA_FLAG_EX_DATA_OPT;
	if ((err = sua_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;
      nnotsupport:
	err = NNOTSUPPORT;
	ptrace(("ERROR: protocol class not supported\n"));
	goto error;
      nbadopt2:
	err = NBADOPT;
	ptrace(("ERROR: invalid protocol class\n"));
	goto error;
      nbadqostype:
	err = NBADQOSTYPE;
	ptrace(("ERROR: QOS structure type not supported\n"));
	goto error;
      nbadopt3:
	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
 *  ---------------------------------------------------------------
 *  IMPLEMENTATION NOTES:- Sequence numbers are actually the address of the
 *  mblk which contains the CR message and contains the contents of this
 *  message as a connection indication.  To find if a particular sequence
 *  number is valid, we walk the connection indication queue looking for a
 *  mblk with the same address as the sequence number.  Sequence numbers are
 *  only valid on the stream for which the connection indication is queued.
 */
STATIC mblk_t *n_seq_check(sua_t * priv, ulong seq)
{
	mblk_t *mp;

	for (mp = bufq_head(&priv->conq); mp && mp != (mblk_t *) seq; mp = mp->b_next);
	usual(mp);
	return (mp);
}
STATIC sua_t *n_tok_check(sua_t * priv, ulong tok)
{
	priv_t *ap;
	queue_t *aq = (queue_t *) tok;

	for (ap = sua_opens; ap && ap->rq != aq; ap = ap->next);
	usual(ap);
	return ((sua_t *) ap);
}

STATIC int n_conn_res(queue_t * q, mblk_t * mp)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	mblk_t *cp;
	sua_t *ap;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_conn_res_t *p = (typeof(p)) mp->b_rptr;
	N_qos_sel_conn_sccp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset);
	uint ap_oldstate;
	uint ap_oldflags;

	if (priv->i_state != NS_WRES_CIND)
		goto noutstate;
	priv->i_state = NS_WACK_CRES;
	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_SCCP)
		goto nbadqostype;
	if (p->QOS_length && p->QOS_length != sizeof(*qos))
		goto nbadopt;
	if (!(cp = n_seq_check(priv, p->SEQ_number)))
		goto nbadseq;
	if (!(ap = n_tok_check(priv, p->TOKEN_value))
	    || (ap != priv && ((1 << ap->i_state) & ~(NSF_UNBND | NSF_IDLE))))
		goto nbadtoken2;
	if (ap->i_state != NS_IDLE || ap->conind)
		goto nbadtoken;
	/* 
	 * protect at least r00t streams from users 
	 */
	if (priv->cred.cr_uid != 0 && ap->cred.cr_uid == 0)
		goto naccess;

	ap_oldstate = ap->i_state;
	ap_oldflags = ap->flags;

	ap->i_state = NS_DATA_XFER;
	ap->flags &= ~(SUA_FLAG_REC_CONF_OPT | SUA_FLAG_EX_DATA_OPT);
	if (p->CONN_flags & REC_CONF_OPT)
		ap->flags |= SUA_FLAG_REC_CONF_OPT;
	if (p->CONN_flags & EX_DATA_OPT)
		ap->flags |= SUA_FLAG_EX_DATA_OPT;
	if ((err = sua_conn_res(q, cp, ap, mp->b_cont)))
		goto badconn;
	mp->b_cont = NULL;	/* absorbed mp->b_cont */
	return n_ok_ack(q, N_CONN_RES, p->SEQ_number, p->TOKEN_value);

      badconn:
	ap->i_state = ap_oldstate;
	ap->flags = ap_oldflags;
	err = err;
	ptrace(("ERROR: couldn't connect\n"));
	goto error;
      naccess:
	err = NACCESS;
	ptrace(("ERROR: no access to accepting queue\n"));
	goto error;
      nbadtoken:
	err = NBADTOKEN;
	ptrace(("ERROR: accepting queue is listening\n"));
	goto error;
      nbadtoken2:
	err = NBADTOKEN;
	ptrace(("ERROR: accepting queue id is invalid\n"));
	goto error;
      nbadseq:
	err = NBADSEQ;
	ptrace(("ERROR: connection ind reference is invalid\n"));
	goto error;
      nbadopt:
	err = NBADOPT;
	ptrace(("ERROR: quality of service options are bad\n"));
	goto error;
      nbadqostype:
	err = NBADQOSTYPE;
	ptrace(("ERROR: quality of service options are bad\n"));
	goto error;
      nbadopt2:
	err = NBADOPT;
	ptrace(("ERROR: quality of service options are bad\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_CONN_RES, err);
}

/* 
 *  N_DISCON_REQ         2 - NC disconnection request
 *  ---------------------------------------------------------
 */
STATIC int n_discon_req(queue_t * q, mblk_t * mp)
{
	sua_t *priv = SUA_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 (priv->i_state) {
	case NS_WCON_CREQ:
		priv->i_state = NS_WACK_DREQ6;
		break;
	case NS_WRES_CIND:
		priv->i_state = NS_WACK_DREQ7;
		break;
	case NS_DATA_XFER:
		priv->i_state = NS_WACK_DREQ9;
		break;
	case NS_WCON_RREQ:
		priv->i_state = NS_WACK_DREQ10;
		break;
	case NS_WRES_RIND:
		priv->i_state = NS_WACK_DREQ11;
		break;
	default:
		goto noutstate;
	}
	if (mlen < sizeof(*p))
		goto einval;
	if (p->RES_length)
		goto nbadaddr;
	if (priv->i_state == NS_WACK_DREQ7 && !(cp = n_seq_check(priv, p->SEQ_number)))
		goto nbadseq;
	/* 
	 *  XXX: What to do with the disconnect reason?  Nothing?
	 */
	if ((err = sua_discon_req(q, cp)))
		goto baddisconn;
	return n_ok_ack(q, N_DISCON_REQ, p->SEQ_number, 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)
{
	sua_t *priv = SUA_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 = sua_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(priv->rq, mp);
		return (0);
	}
	return (-ENOBUFS);
}

STATIC INLINE int n_write(queue_t * q, mblk_t * mp)
{
	sua_t *priv = SUA_PRIV(q);
	int err;

	if (priv->i_state == NS_IDLE)
		goto discard;
	if ((1 << priv->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND))
		goto eproto;
	if ((err = sua_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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	N_qos_sel_data_sccp_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 (priv->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_SCCP)
			qos = NULL;
	}
	if ((1 << priv->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND))
		goto eproto;
	exp = 0;		/* FIXME */
	more = 0;		/* FIXME */
	rcpt = 0;		/* FIXME */
	if ((err = sua_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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	N_qos_sel_data_sccp_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 (priv->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_SCCP)
			qos = NULL;
	}
	if ((1 << priv->i_state) & ~(NSF_DATA_XFER | NSF_WRES_RIND))
		goto eproto;
	exp = 0;		/* FIXME */
	more = 0;		/* FIXME */
	rcpt = 0;		/* FIXME */
	if ((err = sua_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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_bind_req_t *p = (typeof(p)) mp->b_rptr;
	sua_addr_t *a;

	if (priv->i_state != NS_UNBND)
		goto noutstate;
	priv->i_state = NS_WACK_BREQ;
	if (mlen < sizeof(*p))
		goto einval;
	a = (sua_addr_t *) (mp->b_rptr + p->ADDR_offset);
	if ((mlen < p->ADDR_offset + p->ADDR_length)
	    || (p->ADDR_length < sizeof(sua_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 (priv->cred.cr_uid != 0 && !a->ssn)
		goto naccess;
	if ((err = sua_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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	N_unbind_req_t *p = (typeof(p)) mp->b_rptr;

	(void) p;
	if (priv->i_state != NS_IDLE)
		goto noutstate;
	priv->i_state = NS_WACK_UREQ;
	if ((err = sua_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)
{
	sua_t *priv = SUA_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_sccp_t *qos;
	sua_addr_t *dst;
	ulong pclass, options, import, seq, pri;

	if (dlen == 0 || dlen > priv->mtu)
		goto eproto;
	if (priv->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 (priv->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_SCCP)
			qos = NULL;
	}
	pclass = qos ? qos->protocol_class : priv->pclass;
	options = qos ? qos->option_flags : priv->options;
	import = qos ? qos->importance : priv->imp;
	seq = qos ? qos->sequence_selection : priv->sls;
	pri = qos ? qos->message_priority : priv->mp;
	if ((err = sua_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)
{
	sua_t *priv = SUA_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_sccp_t *qos = (typeof(qos)) (mp->b_rptr + p->QOS_offset);

	if (priv->i_state == NS_IDLE)
		priv->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_SCCP)
		goto nbadqostype;
	if (p->QOS_length && p->QOS_length != sizeof(*qos))
		goto nbadopt2;
	if (p->QOS_length && qos->protocol_class > 3)
		goto nbadopt3;
	if (p->QOS_length) {
		if (qos->protocol_class != -1UL)
			priv->pclass = qos->protocol_class;
		if (qos->option_flags != -1UL)
			priv->options = qos->option_flags;
	}
	if (p->OPTMGMT_flags & DEFAULT_RC_SEL)
		priv->flags |= SUA_FLAG_DEFAULT_RC_SEL;
	else
		priv->flags &= ~SUA_FLAG_DEFAULT_RC_SEL;
	return n_ok_ack(q, N_OPTMGMT_REQ, 0, 0);
      nbadopt3:
	err = NBADOPT;
	ptrace(("ERROR: bad protocol class\n"));
	goto error;
      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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_reset_req_t *p = (typeof(p)) mp->b_rptr;

	if (priv->i_state == NS_IDLE)
		goto discard;
	if (priv->i_state != NS_DATA_XFER)
		goto noutstate;
	priv->i_state = NS_WCON_RREQ;
	(void) p;
	(void) mlen;
	if ((err = sua_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)
{
	sua_t *priv = SUA_PRIV(q);
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	N_reset_res_t *p = (typeof(p)) mp->b_rptr;

	if (priv->i_state == NS_IDLE)
		goto discard;
	if (priv->i_state != NS_WRES_RIND)
		goto noutstate;
	priv->i_state = NS_WACK_RRES;
	(void) p;
	(void) mlen;
	if ((err = sua_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 sua_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 SUA_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 sua_n_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	sua_t *priv = SUA_PRIV(q);
	ulong oldstate = priv->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();
		priv->i_state = oldstate;
	}
	return (rtn);
}

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

/* 
 *  -------------------------------------------------------------------------
 *
 *  Other messages
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sua_n_w_other(queue_t * q, mblk_t * mp)
{
	sua_t *priv = SUA_PRIV(q);

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

/* 
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  WRITE PUT and SERVICE (Message from above SUA-User --> SUA-Provider)
 *  -------------------------------------------------------------------------
 */
STATIC int sua_n_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sua_n_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sua_n_w_proto(q, mp);
	case M_FLUSH:
		return sua_w_flush(q, mp);
	case M_IOCTL:
		return sua_n_w_ioctl(q, mp);
	default:
		return sua_n_w_other(q, mp);
	}
	return (-EOPNOTSUPP);
}

/* 
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  -------------------------------------------------------------------------
 */
int sua_n_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	sua_t *priv;
	priv_t *priv, **privp;
	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 (SUA_PRIV(q) != NULL) {
		ptrace(("INFO: Device already open\n"));
		return (0);
	}
	if (sflag == CLONEOPEN) {
		ptrace(("INFO: Clone open in effect\n"));
		cmajor = SUA_CMAJOR;
		cminor = 0;
	}
	if (cmajor == SUA_CMAJOR && cminor == 0 && sflag != CLONEOPEN) {
		ptrace(("INFO: Clone minor opened\n"));
		sflag = CLONEOPEN;
		cminor = 1;
	}
	for (privp = &sua_opens; *privp; privp = &(*privp)->next) {
		ushort dmajor = getmajor((*privp)->u.devid);

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

			if (cminor < dminor)
				break;
			if (cminor == dminor) {
				if (sflag == CLONEOPEN) {
					if (++cminor >= SUA_NMINOR) {
						if (++cmajor >= SUA_CMAJOR + SUA_NMAJOR)
							break;
						cminor = 0;
					}
					continue;
				}
				ptrace(("ERROR: Requested device (%d,%d) in use\n", cmajor,
					cminor));
				return (EIO);
			}
		}
	}
	if (cmajor >= SUA_CMAJOR + SUA_NMINOR) {
		ptrace(("ERROR: No devices available\n"));
		return (ENXIO);
	}
	if (!((void *) priv = (void *) priv = kmalloc(sizeof(*priv), 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(priv, sizeof(*priv));
	SUA_PRIV(q) = SUA_PRIV(WR(q)) = priv;
	priv->rq = q;
	priv->wq = WR(q);
	priv->devid = *devp;
	priv->ops.w_prim = &sua_n_w_prim;
	priv->ops.r_prim = &sua_u_r_prim;
	priv->ifops = &sua_n_ops;
	if ((priv->next = *privp))
		priv->next->prev = &priv->next;
	priv->prev = privp;
	*privp = priv;
	return (0);

}

/* 
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
int sua_n_close(queue_t * q, int flag, cred_t * crp)
{
	sua_t *priv = SUA_PRIV(q);

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

		if ((--priv->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 SUA 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 SUA
	 *  structures associated with the module.  I.e. some SUA bindings.
	 */
	if (priv->rbid)
		unbufcall(xchg(&priv->rbid, 0));
	if (priv->wbid)
		unbufcall(xchg(&priv->wbid, 0));
	if ((*(priv->prev) = priv->next))
		priv->next->prev = priv->prev;
	SUA_PRIV(WR(q)) = SUA_PRIV(RD(q)) = NULL;
	kfree(priv);
	return (0);
}

/* 
 *  =========================================================================
 *
 *  Linux Kernel Module Initialization
 *
 *  =========================================================================
 */
int init_module(void)
{
	cmn_err(CE_NOTE, SUA_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/sua_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: