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


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



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

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

#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 "../debug.h"
#include "../bufq.h"

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

#include "../ua/ua_data.h"
#include "sgp_data.h"
#include "sgp_msg.h"

#define SGP_DESCRIP	"SIGTRAN SGP-Proxy STREAMS MULTIPLEXING DRIVER."
#define SGP_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define SGP_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define SGP_CONTACT	"Brian Bidulock <bidulock@openss7.org>
#define SGP_LICENSE	"GPL"
#define SGP_BANNER	SGP_DESCRIP	"\n" \
			SGP_COPYRIGHT	"\n" \
			SGP_DEVICE	"\n" \
			SGP_CONTACT	"\n"

MODULE_AUTHOR(SGP_CONTACT);
MODULE_DESCRIPTION(SGP_DESCRIP);
MODULE_SUPPORTED_DEVICE(SGP_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(SGP_LICENSE);
#endif

#ifndef INT
#define INT void
#endif

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info sgp_u_minfo = {
	0,				/* Module ID number */
	"sgp-lm",			/* Module name */
	0,				/* Min packet size accepted */
	INFPSZ,				/* Max packet size accepted */
	1,				/* Hi water mark */
	0				/* Lo water mark */
};
static struct module_info sgp_l_minfo = {
	0,				/* Module ID number */
	"sgp-n",			/* Module name */
	0,				/* Min packet size accepted */
	INFPSZ,				/* Max packet size accepted */
	1,				/* Hi water mark */
	0				/* Lo water mark */
};

static int sgp_open(queue_t *, dev_t *, int, int, cred_t *);
static int sgp_close(queue_t *, int, cred_t *);

static INT sgp_u_wput(queue_t *, mblk_t *);
static INT sgp_u_wsrv(queue_t *);

static struct qinit sgp_u_winit = {
	sgp_u_wput,			/* Write put (msg from above) */
	sgp_u_wsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&sgp_u_minfo,			/* Information */
	NULL				/* Statistics */
};

static INT sgp_u_rput(queue_t *, mblk_t *);
static INT sgp_u_rsrv(queue_t *);

static struct qinit sgp_u_rinit = {
	sgp_u_rput,			/* Read put (msg from below) */
	sgp_u_rsrv,			/* Read queue service */
	sgp_open,			/* Each open */
	sgp_close,			/* Last close */
	NULL,				/* Admin (not used) */
	&sgp_u_minfo,			/* Information */
	NULL				/* Statistics */
};

static INT sgp_l_wput(queue_t *, mblk_t *);
static INT sgp_l_wsrv(queue_t *);

static struct qinit sgp_l_winit = {
	sgp_l_rput,			/* Write put (msg from above) */
	sgp_l_rsrv,			/* Write queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&sgp_l_minfo,			/* Information */
	NULL				/* Statistics */
};

static INT sgp_l_rput(queue_t *, mblk_t *);
static INT sgp_l_rsrv(queue_t *);

static struct qinit sgp_l_rinit = {
	sgp_l_rput,			/* Read put (msg from below) */
	sgp_l_rsrv,			/* Read queue service */
	NULL,				/* Each open */
	NULL,				/* Last close */
	NULL,				/* Admin (not used) */
	&sgp_l_minfo,			/* Information */
	NULL				/* Statistics */
};

static struct streamtab sgp_info = {
	&sgp_u_rinit,			/* Upper read queue */
	&sgp_u_winit,			/* Upper write queue */
	&sgp_l_rinit,			/* Lower read queue */
	&sgp_l_winit			/* Lower write queue */
};

/*
 *  =========================================================================
 *
 *  OUTPUT EVENTS
 *
 *  =========================================================================
 */

/*
 *  -------------------------------------------------------------------------
 *
 *  Buffer Allocation
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  BUFSRV calls service routine
 *  ------------------------------------
 */
static void ua_bufsrv(long data)
{
	ua_t *ua = (ua_t *) data;
	if (ua->bid) {
		ua->bid = 0;
		qenable(ua->rq);
		qenable(ua->wq);
	}
}

/*
 *  BUFCALL for enobufs
 *  ------------------------------------
 */
static int ua_bufcall(ua_t * ua, size_t size)
{
	if (!ua->bid)
		ua->bid = bufcall(size, BPRI_MED, *ua_bufsrv, (long) ua);
	return (-ENOBUFS);
}

/*
 *  REUSEB
 *  ------------------------------------
 *  Attempt to reuse a mblk before allocating one.
 */
static int reuseb(ua_t * ua, size_t size, int prior, mblk_t ** mp)
{
	int rtn;
	if (!(*mp) || (*mp)->b_datap->db_lim = (*mp)->b_datap->db_base < size) {
		rtn = 0;
		if (!((*mp) = allocb(size, prior)))
			return ua_bufcall(ua, size);
	} else {
		rtn = 1;
		if ((*mp)->b_cont) {
			freemsg((*mp)->b_cont);
			(*mp)->b_cont = NULL;
		}
		(*mp)->b_wptr = (*mp)->b_rptr = (*mp)->b_datap->db_base;
	}
	return (rtn);
}

/*
 *  SGP LM Upstream Primitives
 *  -----------------------------------
 */
static int lm_ok_ack(ua_t * ua, mblk_t * mp, ulong prim)
{
	int rtn;
	lm_ok_ack_t *p;
	if ((rtn = reuseb(ua, sizeof(*p), BPRI_MED, &mp)) >= 0) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PCPROTO;
		p->prim = LM_OK_ACK;
		p->correct_prim = prim;
		putnext(ua->mq, mp);
	}
	return (rtn);
}
static int lm_error_ack(ua_t * ua, mblk_t * mp, ulong prim, int err)
{
	int rtn;
	lm_error_ack_t *p;
	if ((rtn = reuseb(ua, sizeof(*p), BPRI_MED, &mp)) >= 0) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PCPROTO;
		p->prim = LM_ERROR_ACK;
		p->error_prim = prim;
		p->errno = err < 0 ? LM_SYSERR : err;
		p->unix_error = err < 0 ? -err : 0;
		putnext(ua->mq, mp);
	}
	return (rtn);
}
static int lm_reg_ind(ua_t * ua, mblk_t * mp)
{
	int rtn;
	lm_reg_ind_t *p;
	if ((rtn = reuseb(ua, sizeof(*p), BPRI_MED, &mp)) >= 0) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PCPROTO;
		p->prim = LM_REG_IND;
		p->muxid = ua->u.btm.muxid;
		p->aspid = FIXME;
		p->load = FIXME;
		p->prio = FIXME;
		p->KEY_number = FIXME;
		p->KEY_offset = FIXME;
		p->KEY_length = FIXME;
		putnext(ua->mq, mp);
	}
	return (rtn);
}
static int lm_dereg_ind(ua_t * ua, mblk_t * mp, ulong key)
{
	int rtn;
	lm_dereg_ind_t *p;
	if ((rtn = reuseb(ua, sizeof(*p), BPRI_MED, &mp)) >= 0) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PCPROTO;
		p->prim = LM_DEREG_IND;
		p->muxid = ua->u.btm.muxid;
		p->KEY_number = key;
		putnext(ua->mq, mp);
	}
	return (rtn);
}
static int lm_error_ind(ua_t * ua, mblk_t * mp, int err)
{
	int rtn;
	lm_error_ind_t *p;
	if ((rtn = reuseb(ua, sizeof(*p), BPRI_MED, &mp)) >= 0) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PCPROTO;
		p->prim = LM_ERROR_IND;
		p->muxid = ua->u.btm.muxid;
		p->errno = err < 0 ? LM_SYSERR : err;
		p->unix_error = err < 0 ? -err : 0;
		putnext(ua->mq, mp);
	}
	return (rtn);
}

/*
 *  SGP N-Provider Downstream Primitives
 *  -----------------------------------
 */
static int n_data_req(ua_t * ua, mblk_t * mp, int ppi, int sid, int rcpt, mblk_t * dp)
{
	int rtn;
	N_data_req_t *p;
	N_qos_sel_data_sctp_t *q;
	if ((mp = allocb(sizeof(*p) + sizeof(*q), BPRI_MED))) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PROTO;
		p->PRIM_type = N_DATA_REQ;
		p->DATA_xfer_flags = rcpt ? N_RC_FLAG : 0;
		q = ((typeof(q)) mp->b_wptr)++;
		q->n_qos_type = N_QOS_SEL_DATA_SCTP;
		q->ppi = ppi;
		q->sid = sid;
		q->ssn = 0;
		q->tsn = 0;
		q->more = 0;
		mp->b_cont = dp;
		putnext(ua->wq, mp);
		return (0);
	}
	return (-ENOBUFS);
}
static int n_exdata_req(ua_t * ua, mblk_t * mp, int ppi, int sid, int rcpt, mblk_t * dp)
{
	int rtn;
	N_exdata_req_t *p;
	N_qos_sel_data_sctp_t *q;
	if ((mp = allocb(sizeof(*p) + sizeof(*q), BPRI_MED))) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PROTO;
		mp->b_band = 2;
		p->PRIM_type = N_EXDATA_REQ;
		q = ((typeof(q)) mp->b_wptr)++;
		q->n_qos_type = N_QOS_SEL_DATA_SCTP;
		q->ppi = ppi;
		q->sid = sid;
		q->ssn = 0;
		q->tsn = 0;
		q->more = 0;
		mp->b_cont = dp;
		putnext(ua->wq, mp);
		return (0);
	}
	return (-ENOBUFS);
}
static int n_discon_req(ua_t * ua, mblk_t * mp, ulong seq, void *res_ptr, size_t res_len)
{
	int rtn;
	N_discon_req_t *p;
	if ((rtn = reuseb(ua, sizeof(*p) + res_len, BPRI_MED, &mp)) >= 0) {
		p = ((typeof(p)) mp->b_wptr)++;
		mp->b_datap->db_type = M_PCPROTO;
		p->PRIM_type = N_DISCON_REQ;
		p->DISCON_reason = 0;
		p->RES_length = res_len;
		p->RES_offset = res_len ? sizeof(*p) : 0;
		p->SEQ_number = seq;
		putnext(ua->wq, mp);
	}
	return (rtn);
}

/*
 *  SGP message written for lower stream (need translating).
 */
static int sgp_mgmt_w_err(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_mgmt_w_ntfy(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_snmm_w_duna(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_snmm_w_dava(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_snmm_w_daud(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_snmm_w_scon(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_snmm_w_dupu(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_snmm_w_drst(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_w_aspup_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_w_aspdn_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_w_hbeat_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_w_aspup_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_w_aspdn_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_w_hbeat_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_aspt_w_aspac_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_aspt_w_aspia_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_aspt_w_aspac_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_aspt_w_aspia_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_w_reg_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_w_reg_rsp(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_w_dereg_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_w_dereg_rsp(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}

/*
 *  =========================================================================
 *
 *  SGP State Machine
 *
 *  =========================================================================
 */
/*
 *  =========================================================================
 *
 *  UA Peer --> UA (SGP) Received Messages
 *
 *  =========================================================================
 *
 *  These are procedures for common UA received messages at the SGP.
 */
static int ua_recv_aspac_ack(queue_t * q, mblk_t * mp)
{
	int i;
	struct ua_parm *rc = &ua_parm_results.rc;
	ua_decode_parms(mp);
	if (rc->u.wptr && rc->len > 4) {
		int i;
		uint32_t *p = rc->u.wptr;
		for (i = 0, p = rc->u.wptr; i < (rc->len / 4) - 1; i++, p++) {
			uint32_t rcval = ntohl(*p++);
			/* 
			 *  Check for AS which belong to the SGP by routing
			 *  context.  Find AS in the AS_WACK_ASPAC state.
			 *  Move them to the AS_ACTIVE state.  I don't know
			 *  what the purpose of the returned traffic mode
			 *  type is...  Is this the mode of the SGP?
			 */
		}
	} else {
	}
	freemsg(mp);
	return (0);
}

/*
 *  ASPT ASPIA Ack
 *  -------------------------------------------------------------------------
 *  Routing Context	Optional
 *  INFO String		Optional
 */
static int ua_recv_aspia_ack(queue_t * q, mblk_t * mp)
{
	int i;
	struct ua_parm *rc = &ua_parms.rc;
	ua_decode_parms(mp);
	if (rc->u.wptr && rc->len >= 4) {
		int i;
		uint32_t *p = rc->u.wptr;
		for (i = 0, p = rc->u.wptr; i < (rc->len / 4) - 1; i++, p++) {
			uint32_t rcval = ntohl(*p++);
			/* 
			 *  Find the RC in the list of AS which belong to this
			 *  SGP and see if it is in the state AS_STATE_WACK_ASPIA.
			 *  If it is, put it in the state AS_STATE_INACTIVE.
			 */
		}
	} else {
		/* 
		 *  RC was not provided.  This is the default routing context
		 *  case.  Do we only have one AS?  Only one AS in the
		 *  AS_STATE_WACK_ASPIA?
		 */
	}
	freemsg(mp);
	return (0);
}

/*
 *  MGMT ERR
 *  -------------------------------------------------------------------------
 *  Error Code		Mandatory
 *  Diagnostic Info	Optional
 */
static int ua_recv_err(queue_t * q, mblk_t * mp)
{
	sp_t *sp = ((pp_t *) q->q_ptr)->u.sp;
	ua_decode_parms(mp);
	if (!ua_parms.ecode.u.wptr)
		return -EINVAL;
	switch (ua_parms.ecode.val) {
		/* 
		 *  These values are from ua-07.
		 */
	case 0x01:		/* Invalid Version */
	case 0x05:		/* Unsupported Traffic Handling Mode */
	case 0x08:		/* Invalid Routing Context */
		/* 
		 *  If I am trying something, give up.
		 */
	case 0x0c:		/* Unexpected Parameter */
		/* 
		 *  TODO: Might be because we a using an extension
		 *  that the other end doesn't recognize.  Go after
		 *  the diagnostics parameter and see if we get the
		 *  parameter.  Then see if we can discover features
		 *  of the other end from which parameter was
		 *  unexpected.
		 */
	default:
	case 0x02:		/* n/a */
	case 0x03:		/* Unexpected Message Class */
	case 0x04:		/* Invalid Message Type Class */
	case 0x06:		/* Unexpected Message */
	case 0x07:		/* Protocol Error */
	case 0x09:		/* Invalid Stream Identifier */
	case 0x0b:		/* Parameter Field Error */
	case 0x0d:		/* Duplicated Parameter */
	case 0x0e:		/* Error - ASP Identifier Required */
	case 0x0f:		/* Error - Invalid ASP Identifier */
	case 0x10:		/* Refused - ASP Identifier Required */
		/* 
		 *  Some of these can't happen (we always send
		 *  optional fields).  Don't give up.
		 */
	}
	/* 
	 *  TODO: Notify management.
	 */
	freemsg(mp);
	return (0);
/*
 *  MGMT ERR
 *  -------------------------------------------------------------------------
 *  IMPLEMENTATION NOTE:-  We want to report all errors to management.  Some
 *  errors are considered fatal errors.  That is we will stop all processes,
 *  and put everything associated with the ASP/SGP/SP into the Down state,
 *  even drop the SCTP association.
 */
	static int m3ua_err(queue_t * q, mblk_t * mp) {
		m3ua_parms_t parms;
		parm_t *ecode = &parms->common.ecode;
		parm_t *diag = &parms->common.diag;

		m3ua_decode_parms(msg, &parms);

		if (!ecode->u.wptr) {
			/* missing mandatory parameter */
			return (-EINVAL);
		}
		switch (*ecode->u.wptr) {
		case UA_ECODE_INVALID_VERSION:
		case UA_ECODE_INVALID_IID_OR_NTWK_APP:
		case UA_ECODE_UNSUPPORTED_MESSAGE_CLASS:
		case UA_ECODE_UNSUPPORTED_MESSAGE_TYPE:
		case UA_ECODE_UNSUPPORTED_TRAFFIC_MODE:
		case UA_ECODE_UNEXPECTED_MESSAGE:
		case UA_ECODE_PROTOCOL_ERROR:
		case UA_ECODE_INVALID_ROUTING_CONTEXT:
		case UA_ECODE_INVALID_STREAM_IDENTIFIER:
		case UA_ECODE_INVALID_PARAMETER_VALUE:
		case M3UA_ECODE_REFUSED_MANAGEMENT_BLOCKING:
		case M3UA_ECODE_UNKNOWN_ROUTING_CONTEXT:
		case M3UA_ECODE_INVALID_ASPID:
		default:
			/* error invalid error???? */
			return (-EINVAL);
		}

		return (-EOPNOTSUPP);
	}
}

/*
 *  MGMT NTFY
 *  -------------------------------------------------------------------------
 *  Status		Mandatory
 *  ASP Identifier	Optional
 *  Routing Context	Optional
 *  Info String		Optional
 */
static int ua_recv_ntfy(queue_t * q, mblk_t * mp)
{
	int i, err = 0;
	uint32_t *rc;
	struct ua_parm *rc = &ua_parm_results.rc;
	struct ua_parm *aspid = &ua_parm_results.aspid;
	sp_t *sp = ((pp_t *) q->q_ptr)->u.sp;
	ua_decode_parms(mp);
	if (!ua_parm_results.status.u.wptr)
		return -EINVAL;
	switch (ua_parm_results.status.val) {
	case 0x00010001:	/* Application Server Down (AS-Down) */
		err = ua_ntfy_as_state_change(q, rc, AS_STATE_DOWN);
		break;
	case 0x00010002:	/* Application Server Inactive (AS-Inactive) */
		err = ua_ntfy_as_state_change(q, rc, AS_STATE_INACTIVE);
		break;
	case 0x00010003:	/* Application Server Active (AS-Active) */
		err = ua_ntfy_as_state_change(q, rc, AS_STATE_ACTIVE);
		break;
	case 0x00010004:	/* Application Server Pending (AS-Pending) */
		err = ua_ntfy_as_state_change(q, rc, AS_STATE_PENDING);
		break;
	case 0x00020001:	/* Insufficient ASP resources active in AS */
		err = ua_ntfy_too_few_asps(q, rc, aspid);
		break;
	case 0x00020002:	/* Alternative ASP Active */
		err = ua_ntfy_alternate_asp(q, aspid);
		break;
	case 0x00020003:	/* ASP Failure */
		err = ua_ntfy_asp_failure(q, aspid);
		break;
	case 0x00020004:	/* Minimum ASP resources active in AS */
		err = ua_ntfy_as_vulnerable(q, rc);
		break;
	}
	if (err)
		return (err);
	freemsg(mp);
	return (0);
}

/*
 *  MGMT NTFY
 *  -------------------------------------------------------------------------
 *  IMPLEMENTATION NOTES:- Of these notifications, the changes in the state of
 *  an Application Server as viewed by the SGP can be tracked at the ASP/IPSP.
 *  Also, the change in state of specific ASP within the AS are indicated by
 *  other notifications.  We want to do two things with these notifications:
 *  first, record the state change so that if we are later interrogated by the
 *  application or layer management, we can report the current status of the
 *  AS and ASPs within it, second, we inform both the application and layer
 *  management of the state change.  We take no other action.  The reason we
 *  take no other action is because we ae ill-equipped with custom
 *  configuration information to determine within the kernel what the proper
 *  response of the system is to these notifications.  For example, if an we
 *  are om the ASP_INACTIVE state with respect to an Application Server and we
 *  get a NTFY(Minimum ASP resources available in AS) notification, the layer
 *  manager may choose to start an application and have it activate the ASP
 *  within the Application Server to reduce the vulnerability of the AS.
 *  Booting applications is not something to be performed here...
 *
 *  Actually, we don't care if the ASP Id or Routing Context is valid or not.
 *  Let the application or layer manager figure that out.
 */
static int m3ua_ntfy(queue_t * q, mblk_t * mp)
{
	int i;
	as_t *as;
	uint event, state;
	uint muxid = Q_MUXID(q);

	m3ua_parms_t parms;
	parm_t *rc = &parms->common.rc;
	parm_t *aspid = &parms->common.aspid;
	parm_t *status = &parms->common.status;

	if (Q_MODE(q) == Q_MODE_SG) {
		/* unexpected message, AS do not notify SG */
		return (-EINVAL);
	}
	if (!canput(m3ua_ctrlq))
		return (-EBUSY);
	m3ua_decode_parms(msg, &parms);
	if (!status->u.wptr) {
		/* missing mandatory parameter */
		return (-EINVAL);
	}
	if (rc->len > 0 && (rc->len & 0x3))
		/* badly formatted parameter */
		return (-EINVAL);
	switch (*status->u.wptr) {
	case UA_STATUS_AS_DOWN:
		event = ASE_DOWN;
		state = AS_DOWN;
		break;
	case UA_STATUS_AS_INACTIVE:
		event = ME_AS_INACTIVE;
		state = AS_INACTIVE;
		break;
	case UA_STATUS_AS_ACTIVE:
		event = ME_AS_ACTIVE;
		state = AS_ACTIVE;
		break;
	case UA_STATUS_AS_PENDING:
		event = ME_AS_PENDING;
		state = AS_PENDING;
		break;
	case UA_STATUS_AS_INSUFFICIENT_ASPS:
		event = ME_AS_CRITICAL;
		state = AS_CRITICAL;
		break;
	case UA_STATUS_ALTERNATE_ASP_ACTIVE:
		event = ME_ASP_OVERRIDE;
		state = -1;
		break;
	case UA_STATUS_ASP_FAILURE:
		event = ME_ASP_FAILURE;
		state = -1;
		break;
	case UA_STATUS_AS_MINIMUM_ASPS:
		event = ME_AS_VULNERABLE;
		state = AS_VULNERABLE;
		break;
	default:
		/* invalid status */
		return (-EINVAL);
	}
	if (!rc->u.wptr || rc->len < sizeof(uint32_t)) {
		/* No routing contexts in message, is it implied? */
		if (!(as = m3ua_as_default(q)))
			/* missing necessary routing context */
			return (-EINVAL);
		if (lm_as_event_ind(muxid, event, as->id, aspid->val))
			return (-ENOBUFS);
		if (state != -1)
			as->state = state;
		return (0);
	}
	for (i = 0; i < rc->len >> 2; i++) {
		if (!(as = m3ua_as_lookup(q, rc)))
			/* invalid routing context */
			continue;
		if (lm_as_event_ind(muxid, event, as->id, aspid->val))
			return (-ENOBUFS);
		if (state != -1)
			as->state = state;
	}
	return (0);

}

/*
 *  =========================================================================
 *
 *  INPUT EVENTS
 *
 *  =========================================================================
 */

/*
 *  SGP message read for upper stream (need translating).
 */
static int sgp_mgmt_r_err(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_mgmt_r_ntfy(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_asps_r_aspup_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	as_t *as = ua->u.sp;
}
static int sgp_asps_r_aspdn_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}

/*
 *  ASPS HBEAT Req
 *  -------------------------------------------------------------------------
 *  Heartbeat Data	Optional
 */
static int sgp_asps_r_hbeat_req(queue_t * q, mblk_t * mp)
{
	int err;
	mblk_t *rp;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	ua_t *ua = (ua_t *) q->q_ptr;
	sgp_t *sgp = ua->u.spp;
	/* 
	 *  This is a simple matter of turning around the heartbeat request.
	 *  We can just jam the message type and send it back.  Don't even
	 *  pull out the parameters.
	 */

}

/*
 *  ASPS ASPUP Ack
 *  -------------------------------------------------------------------------
 *  ASP Identifier	Optional (But must be used sometimes)
 *  Info String		Optional
 */
static int sgp_asps_r_aspup_ack(queue_t * q, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	ua_t *ua = (ua_t *) q->q_ptr;
	sgp_t *sgp = ua->u.spp;
	if (sgp && sgp->state == ASP_WACK_ASPUP) {
		fixme(("We should also check that the ASPId is valid\n"));
		as->state = ASP_INACTIVE;
		if ((err = sgp_recalc_as_state(as))) {
			as->state = ASP_WACK_ASPUP;
			return (err);
		}
		fixme(("Notify LM of SGP UP with INFO string.\n"));
		return (0);
	}
	return ua_reply_mgmt_err(q, UA_ECODE_UNEXPECTED_MESSAGE, mp->b_rptr, mlen);
}

/*
 *  ASPS ASPDN Ack
 *  -------------------------------------------------------------------------
 *  Info		Optional
 */
static int sgp_asps_r_aspdn_ack(queue_t * q, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	ua_t *ua = (ua_t *) q->q_ptr;
	sgp_t *sgp = ua->u.spp;
	if (sgp && sgp->state == ASP_WACK_ASPDN) {
		sgp->state = ASP_DOWN;
		if ((err = sgp_recalc_as_state(sgp))) {
			sgp->state = ASP_WACK_ASPDN;
			return (err);
		}
		fixme(("Notify LM of SGP DN with INFO string.\n"));
		return (0);
	}
	return ua_reply_mgmt_err(q, UA_ECODE_UNEXPECTED_MESSAGE, mp->b_rptr, mlen);
}

/*
 *  ASPS HBEAT Ack
 *  -------------------------------------------------------------------------
 *  Heartbeat Data	Optional
 */
static int sgp_asps_r_hbeat_ack(queue_t * q, mblk_t * mp)
{
	int err;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	ua_t *ua = (ua_t *) q->q_ptr;
	sgp_t *sgp = ua->u.spp;
	/* 
	 *  Check the heartbeat data.  There are a number of occasions for
	 *  which we send heartbeats.  There is a process waiting for the
	 *  response based on the heartbeat data or there is not.  This needs
	 *  be done in parallel for some applications (changeback).  We should
	 *  have a process list with ids from the data.  That way we could use
	 *  the data to index the function to call here...
	 *
	 *  Well..., normally we do this to flush an routing context traffic
	 *  flow.  If there is heartbeat data, extract the routing contexts
	 *  out of the heartbeat data and find AS in the AS_WACK_BEAT state.
	 */
	if (sgp && sgp->state == ASP_WACK_BEAT) {
		sp->state = ASP_ACTIVE;
		fixme(("Process hbeat ack\n"));
		return (0);
	}
	return ua_reply_mgmt_err(q, UA_ECODE_UNEXPECTED_MESSAGE, mp->b_rptr, mlen);
}
static int sgp_aspt_r_aspac_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_aspt_r_aspia_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}

/*
 *  ASPT ASPAC Ack
 *  -------------------------------------------------------------------------
 *  Traffic Mode Type		Optional
 *  Routing Context/IIDs	Optional
 *  INFO String			Optional
 */
static int sgp_aspt_r_aspac_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	sgp_t *sgp = ua->u.spp;
	struct ua_parm *rc = NULL;
	struct ua_parm *tmode = NULL;
	struct ua_parms_t parms;
	/* 
	 *  IMPLEMENTATION NOTES:-  The Traffic Mode Type parameter is
	 *  optional in the SGP Active Ack.  Nowhere in any of the UA drafts
	 *  does it say what this parameter is supposed to contain nor what
	 *  the SGP is supposed to do with it.
	 */
	if ((err = ua_decode_parms(&parms, mp)))
		return (err);

	/* 
	 *  M2UA uses IID, M2UA/ISUA/SUA/TUA use RC.
	 */
	if (parms.iid.u.wptr && parms.iid.len >= sizeof(rc->val))
		rc = &parms.iid;
	else if (parms.rc.u.wptr && parms.rc.len >= sizeof(rc->val))
		rc = &parms.rc;
	if (parms.tmode.u.wptr && parms.tmode.len >= sizeof(tmode->val))
		tmode = &parms.tmode;
	if (rc) {
		/* 
		 *  Walk through all of the RCs in the message and perform the
		 *  function on the associated AS.
		 */
		int i;
		uint32_t *p;
		for (i = 0, p = rc->u.wptr; i < (rc->len / 4) - 1; i++, p++) {
			uint32_t rcval = ntohl(*p);
			/* 
			 *  Check for an AS which belongs to the SGP by
			 *  routing context.  Find AS in the AS_WACK_ASPAC
			 *  state.  Move them to the AS_ACTIVE state and stop
			 *  any t_ack timers.  I don't know what the purpose
			 *  of the returned traffic mode type is... Is it the
			 *  mode of the SGP?
			 */
			gp_t *gp;
			for (gp = sgp->gp; gp && gp->sp->rc ==; gp = gp->sp_next) {
			}
		}
	} else {
		/* 
		 *  Walk through all the AS assigned to the SGP and perform
		 *  the function for the associated AS.
		 */
		gp_t *gp;
		for (gp = sgp->gp; gp; gp = gp->sp_next) {
			as_t *as = gp->sp;
			if (!tmode || tmode->val == as->tmode) {
				switch (gp->state) {
				case ASP_INACTIVE:
					fixme(("Send an ERR message\n"));
					continue;
				case ASP_ACTIVE:
				case ASP_WACK_HBEAT:
					/* ignore */
					continue;
				case ASP_WACK_ASPAC:
					gp->state = AS_ACTIVE;
					if (gp->t_ack)
						untimeout(xchg(&gp->t_ack, 0));
					fixme(("Recalculate state\n"));
					continue;
				case ASP_WACK_ASPIA:
					fixme(("Send an ERR and another ASPIA\n"));
					continue;
				case ASP_DOWN:
				case ASP_WACK_ASPUP:
				case ASP_WACK_ASPDN:
				default:
					ptrace(("This is a software error\n"));
					continue;
				}
			}	/* wrong traffic mode type */
		}
	}
	/* 
	 *  Dig out the RC/IIDs if any.  If there are no RC/IIDs, then the
	 *  ASPAC Ack corresponds to all of the AS to which this SGP is
	 *  configured.
	 *
	 *  Walk through the AS that correspond to the RC/IIDs.  And perform
	 *  the ASPAC Ack procedure.  If there are any invalid RC/IIDs,
	 *  respond to them with ERR messges indicating the RC/IID affected.
	 *  If the TMT is provided and is invalid for any of the RC/IIDs,
	 *  then response with appropriate ERR messages.
	 */
	if (sgp) {
		switch (sgp->state) {
		case ASP_DOWN:
		case ASP_INACTIVE:
		case ASP_WACK_BEAT:
		case ASP_WACK_ASPDN:
		case ASP_WACK_ASPUP:
		case ASP_WACK_ASPIA:
		case ASP_WACK_ASPAC:
		}
	}
}
static int sgp_aspt_r_aspia_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_r_reg_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_r_reg_rsp(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_r_dereg_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int sgp_rkmm_r_dereg_rsp(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}

static int sgp_recv_msg(queue_t * q, mblk_t * mp)
{
	switch (class) {
	case UA_CLASS_MGMT:
		switch (msgid) {
		case UA_MSGID_MGMT_ERR:
		case UA_MSGID_MGMT_NTFY:
		}
		break;
	case UA_CLASS_XFER:
		switch (msgid) {
		case UA_MSGID_XFER_DATA:
		}
		break;
	case UA_CLASS_SNMM:
		switch (msgid) {
		case UA_MSGID_SNMM_DUNA:
		case UA_MSGID_SNMM_DAVA:
		case UA_MSGID_SNMM_DAUD:
		case UA_MSGID_SNMM_SCON:
		case UA_MSGID_SNMM_DUPU:
		case UA_MSGID_SNMM_DRST:
		}
		break;
	case UA_CLASS_ASPS:
		switch (msgid) {
		case UA_MSGID_ASPS_ASPUP_REQ:
		case UA_MSGID_ASPS_ASPDN_REQ:
		case UA_MSGID_ASPS_HBEAT_REQ:
		case UA_MSGID_ASPS_ASPUP_ACK:
		case UA_MSGID_ASPS_ASPDN_ACK:
		case UA_MSGID_ASPS_HBEAT_ACK:
		}
		break;
	case UA_CLASS_ASPT:
		switch (msgid) {
		case UA_MSGID_ASPT_ASPAC_REQ:
		case UA_MSGID_ASPT_ASPIA_REQ:
		case UA_MSGID_ASPT_ASPAC_ACK:
		case UA_MSGID_ASPT_ASPIA_ACK:
		}
		break;
	case UA_CLASS_Q921:
		switch (msgid) {
		}
		break;
	case UA_CLASS_MAUP:
		switch (msgid) {
		case M2UA_MSGID_MAUP_DATA:
		case M2UA_MSGID_MAUP_ESTAB_REQ:
		case M2UA_MSGID_MAUP_ESTAB_CON:
		case M2UA_MSGID_MAUP_REL_REQ:
		case M2UA_MSGID_MAUP_REL_CON:
		case M2UA_MSGID_MAUP_REL_IND:
		case M2UA_MSGID_MAUP_STATE_REQ:
		case M2UA_MSGID_MAUP_STATE_CON:
		case M2UA_MSGID_MAUP_STATE_IND:
		case M2UA_MSGID_MAUP_RETR_REQ:
		case M2UA_MSGID_MAUP_RETR_CON:
		case M2UA_MSGID_MAUP_RETR_IND:
		case M2UA_MSGID_MAUP_RETR_COMP_IND:
		case M2UA_MSGID_MAUP_CONG_IND:
		case M2UA_MSGID_MAUP_DATA_ACK:
		}
		break;
	case UA_CLASS_CNLS:
		switch (msgid) {
		case SUA_MSGID_CNLS_CLDT:
		case SUA_MSGID_CNLS_CLDR:
		}
		break;
	case UA_CLASS_CONS:
		switch (msgid) {
		case SUA_MSGID_CONS_CORE:
		case SUA_MSGID_CONS_COAK:
		case SUA_MSGID_CONS_COREF:
		case SUA_MSGID_CONS_RELRE:
		case SUA_MSGID_CONS_RELCO:
		case SUA_MSGID_CONS_RESCO:
		case SUA_MSGID_CONS_RESRE:
		case SUA_MSGID_CONS_CODT:
		case SUA_MSGID_CONS_CODA:
		case SUA_MSGID_CONS_COERR:
		case SUA_MSGID_CONS_COIT:
		}
		break;
	case UA_CLASS_RKMM:
		switch (msgid) {
		case UA_MSGID_RKMM_REG_REQ:
		case UA_MSGID_RKMM_REG_RSP:
		case UA_MSGID_RKMM_DEREG_REQ:
		case UA_MSGID_RKMM_DEREG_RSP:
		}
		break;
	case UA_CLASS_TDHM:
		switch (msgid) {
		case TUA_MSGID_TDHM_UNI:
		case TUA_MSGID_TDHM_BEG:
		case TUA_MSGID_TDHM_CON:
		case TUA_MSGID_TDHM_END:
		case TUA_MSGID_TDHM_U_ABT:
		case TUA_MSGID_TDHM_P_ABT:
		case TUA_MSGID_TDHM_NOT:
		}
		break;
	case UA_CLASS_TCHM:
		switch (msgid) {
		case TUA_MSGID_TCHM_IVK:
		case TUA_MSGID_TCHM_RES:
		case TUA_MSGID_TCHM_U_ERR:
		case TUA_MSGID_TCHM_REJ:
		}
		break;
	}
}

/*
 *  SGP LM Downstream Primitives
 *  -----------------------------------
 */
static int lm_as_add_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_as_add_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_as_del_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_as_del_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_proc_add_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_proc_add_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_proc_del_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_proc_del_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_link_add_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_link_add_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_link_del_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_link_del_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_route_add_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_route_add_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_route_del_req(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_route_del_req_t *p = (typeof(p)) mp->b_rptr;
}
static int lm_reg_res(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	size_t mlen = mp->b_wptr - mp->b_rptr;
	lm_reg_res_t *p = (typeof(p)) mp->b_rptr;
}

/*
 *  SGP N-Provider Upstream Primitives
 *  -----------------------------------
 */
static int n_data_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	if ((err = sgp_recv_msg(ua, mp->b_cont)))
		return (err);
	mp->b_cont = NULL;
	return (0);
}
static int n_exdata_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	if ((err = sgp_recv_msg(ua, mp->b_cont)))
		return (err);
	mp->b_cont = NULL;
	return (0);
}
static int n_datack_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	return sgp_recv_ack(ua, mp);
}

/*
 *  NOTE:- If we get any other indication or confirmation we are not prepared
 *  to handle it.  We error out the stream, place the message back on the
 *  queue (for the configuration daemon to deal with), and disable the queue.
 *  We notify the configuration daemon on the control stream (if possible) and
 *  wait for the configuration daemon to unlink the stream and deal with
 *  things.
 */

static int n_discon_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
}
static int n_reset_ind(queue_t * q, mblk_t * mp)
{
	int rtn;
	ua_t *ua = (ua_t *) q->q_ptr;
	return n_discon_req(ua, mp, 0, NULL, 0);
}
static int n_other_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	ptrace(("SGP received unexpected primitive %ld on lower RD(q) %d\n",
		*((long *) mp->b_rptr), ua->id.mux));
	if ((err = ua_hangup(ua)))
		return (err);
	noenable(q);
	putbq(q, mp);
	return (0);
}
static int n_conn_ind(queue_t * q, mblk_t * mp)
{
	int err;
	N_discon_req_t *p;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_CONN_IND on lower RD(q) %d\n", ua->id.mux));
	fixme(("We need to error out the stream.\n"));
	return (-EFAULT);
}
static int n_conn_con(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_CONN_CON on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}
static int n_info_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_INFO_ACK on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}
static int n_bind_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_BIND_ACK on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}
static int n_error_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_ERROR_ACK on lower RD(q) %d\n", ua->id.mux));
	fixme(("We need to error out the stream.\n"));
	return (-EFAULT);
}
static int n_ok_ack(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_OK_ACK on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}
static int n_unitdata_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_UNITDATA_IND on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}
static int n_uderror_ind(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_UDERROR_IND on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}
static int n_reset_con(queue_t * q, mblk_t * mp)
{
	int err;
	ua_t *ua = (ua_t *) q->q_ptr;
	(void) ua;
	(void) mp;
	ptrace(("SGP received N_RESET_CON on lower RD(q) %d\n", ua->id.mux));
	return (-EFAULT);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCDATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_iocdata(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	ptrace(("Received M_IOCDATA on upper WR(q)\n"));
	return (-EOPNOTSUPP);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static ua_t *sgp_links_list = NULL;

static int sgp_u_w_ioctl(queue_t * q, mblk_t * mp)
{
	int err = EOPNOTSUPP;
	struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
	switch (_IOC_TYPE(iocp->ioc_cmd)) {
	case __SID:
	{
		lp_t *lp;
		ua_t *ua = (ua_t *) q->q_ptr;
		struct linkblk *lb = (struct linkblk *)
		    (mp->b_cont ? mp->b_cont->b_rptr : NULL);
		switch (iocp->ioc_cmd) {
		case I_PLINK:
			err = EPERM;
			if (iocp->ioc_cr->cr_uid != 0)
				break;
		case I_LINK:
			err = 0;
			if (lb->l_qbot->q_ptr != NULL)
				break;	/* already linked */
			if ((lp = kmem_cache_alloc(ua_pp_cachep, SLAB_ATOMIC))) {
				bzero(lp, sizeof(lp_t));
				if ((lp->next = sgp_links_list))
					lp->next->prev = &lp->next;
				lp->prev = &sgp_links_list;
				sgp_links_list = lp;
				lp->id.mux = lb->l_index;
				lp->rq = RD(lb->l_qbot);
				lp->wq = WR(lb->l_qbot);
				lp->mq = RD(q);
				lp->rq->q_ptr = lp->wq->q_ptr = lp;
			}
			err = ENOMEM;
			break;
		case I_PUNLINK:
			err = EPERM;
			if (iocp->ioc_cr->cr_uid != 0)
				break;
		case I_UNLINK:
			err = 0;
			if (!(lp = (lp_t *) lb->l_qbot->q_ptr))
				break;	/* already unlinked */
			fixme(("More thinks to unlink\n"));
			if ((*(lp->prev) = lp->next))
				lp->next->prev = lp->prev;
			lp->prev = NULL;
			lp->next = NULL;
			if (lp->bid)
				unbufcall(lp->bid);
			lp->rq->q_ptr = lp->wq->q_ptr = NULL;
			kmem_cache_free(ua_pp_cachep, lp);
			qenable(lp->rq);
			qenable(lp->wq);
			break;
		}
	}
/*		case SL_IOC_MAGIC:	*//* can't control these thru SGP */
/*		case SDT_IOC_MAGIC:	*//* can't control these thru SGP */
/*		case SDL_IOC_MAGIC:	*//* can't control these thru SGP */
/*		case DEV_IOC_MAGIC:	*//* can't control these thru SGP */
	}
	if (err) {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = err;
		iocp->ioc_rval = -1;
	} else {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	}
	qreply(q, mp);
	return (1);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_proto(queue_t * q, mblk_t * mp)
{
	switch (*((long *) mp->b_rptr)) {
	case LM_AS_ADD_REQ:
		return lm_as_add_req(q, mp);
	case LM_AS_DEL_REQ:
		return lm_as_del_req(q, mp);
	case LM_PROC_ADD_REQ:
		return lm_proc_add_req(q, mp);
	case LM_PROC_DEL_REQ:
		return lm_proc_del_req(q, mp);
	case LM_LINK_ADD_REQ:
		return lm_link_add_req(q, mp);
	case LM_LINK_DEL_REQ:
		return lm_link_del_req(q, mp);
	case LM_ROUTE_ADD_REQ:
		return lm_route_add_req(q, mp);
	case LM_ROUTE_DEL_REQ:
		return lm_route_del_req(q, mp);
	case LM_REG_RES:
		return lm_reg_res(q, mp);
	}
	return (-EOPNOTSUPP);
}
static int sgp_l_r_proto(queue_t * q, mblk_t * mp)
{
	switch (*((long *) mp->b_rptr)) {
	case N_DATA_IND:
		return n_data_ind(q, mp);
	case N_EXDATA_IND:
		return n_exdata_ind(q, mp);
	case N_DATACK_IND:
		return n_datack_ind(q, mp);
	case N_CONN_IND:
	case N_CONN_CON:
	case N_DISCON_IND:
	case N_INFO_ACK:
	case N_BIND_ACK:
	case N_ERROR_ACK:
	case N_OK_ACK:
	case N_UNITDATA_IND:
	case N_UDERROR_IND:
	case N_RESET_IND:
	case N_RESET_CON:
	default:
		return n_other_ind(q, mp);
	}
}
static int sgp_u_r_proto(queue_t * q, mblk_t * mp)
{
	return (5);
}
static int sgp_l_w_proto(queue_t * q, mblk_t * mp)
{
	return (5);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_pcproto(queue_t * q, mblk_t * mp)
{
	return sgp_u_w_proto(q, mp);
}
static int sgp_l_r_pcproto(queue_t * q, mblk_t * mp)
{
	return sgp_l_r_proto(q, mp);
}
static int sgp_u_r_pcproto(queue_t * q, mblk_t * mp)
{
	return (4);
}
static int sgp_l_w_pcproto(queue_t * q, mblk_t * mp)
{
	return (4);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_data(queue_t * q, mblk_t * dp)
{
	(void) q;
	(void) dp;
	ptrace(("SGP received M_DATA on upper WR(q)\n"));
	return (-EOPNOTSUPP);
}

/*
 *  Any M_DATA read by the lower N provider must be converted to M_PROTO to
 *  avoid interfering with the internal interface.
 */
static int sgp_l_r_data(queue_t * q, mblk_t * dp)
{
	int err;
	mblk_t *mp;
	N_data_ind_t *p;
	if ((mp = allocb(sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = N_DATA_IND;
		p->DATA_xfer_flags = 0;
		mp->b_cont = dp;
		switch ((err = n_data_ind(q, mp))) {
		case 0:
			freeb(mp);
			return (0);
		case 1:
			break;
		case 2:
			freeb(mp);
			break;
		case 3:
			if (!q->q_next) {
				qreply(q, mp);
				break;
			}
		case 4:
			if (q->q_next) {
				putnext(q, mp);
				break;
			}
		case 5:
			if (canputnext(q)) {
				putnext(q, mp);
				break;
			}
		default:
			freeb(mp);
			return (err);
		}
		return (1);
	}
	return (-ENOBUFS);
}
static int sgp_u_r_data(queue_t * q, mblk_t * mp)
{
	return (5);
}
static int sgp_l_w_data(queue_t * q, mblk_t * mp)
{
	return (5);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_ctl(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	rare();
	return (-EOPNOTSUPP);
}
static int sgp_l_r_ctl(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	rare();
	return (-EOPNOTSUPP);
}
static int sgp_u_r_ctl(queue_t * q, mblk_t * mp)
{
	return sgp_u_r_data(q, mp);
}
static int sgp_l_w_ctl(queue_t * q, mblk_t * mp)
{
	return sgp_l_w_data(q, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_HANGUP Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_hangup(queue_t * q, mblk_t * mp)
{
	/* 
	 *  We need to treat this as a close and do all the things that we
	 *  would do before deallocating the structure (except deallocating
	 *  the structure.)
	 */
	fixme(("Stuff to do here...\n"));
	return (-EFAULT);
}
static int sgp_l_r_hangup(queue_t * q, mblk_t * mp)
{
	/* 
	 *  We need to propagate the hangup to all users of this provider.
	 */
	fixme(("Stuff to do here...\n"));
	return (-EFAULT);
}
static int sgp_u_r_hangup(queue_t * q, mblk_t * mp)
{
	return (4);
}
static int sgp_l_w_hangup(queue_t * q, mblk_t * mp)
{
	/* 
	 *  This is the same effect as a close on a linked stream.  We need to
	 *  do all the things that would otherwise be done if the stream was
	 *  closed.  The other thing that we could do is write drivers to
	 *  accept M_HANGUP written and treat them as a close (without
	 *  deallocation of the private structure).
	 */
	fixme(("Stuff to do here...\n"));
	return (4);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_l_r_error(queue_t * q, mblk_t * mp)
{
	/* 
	 *  The provider stream has errored out, so we need to propagate the
	 *  error to all users of this stream.
	 */
	fixme(("Stuff to do here...\n"));
	return (-EFAULT);
}
static int sgp_u_r_error(queue_t * q, mblk_t * mp)
{
	return (4);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_m_flush(queue_t * q, mblk_t * mp, const uint8_t mflag, const uint8_t oflag);
{
	if (mp->b_rptr[0] & mflag) {
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHALL);
		else
			flushq(q, FLUSHALL);
		if (q->q_next) {
			putnext(q, mp);
			return (1);
		}
		mp->b_rptr[0] &= ~FLUSHW;
	}
	if (mp->b_rptr[0] & oflag && !(mp->b_flag & MSGNOLOOP)) {
		queue_t *oq = q->q_other;
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(oq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(oq, FLUSHALL);
		mp->b_flag |= MSGNOLOOP;
		qreply(q, mp);
		return (1);
	}
	return (0);
}

static int sgp_u_w_flush(queue_t * q, mblk_t * mp)
{
	return sgp_m_flush(q, mp, FLUSHW, FLUSHR);
}
static int sgp_l_r_flush(queue_t * q, mblk_t * mp)
{
	return sgp_m_flush(q, mp, FLUSHR, FLUSHW);
}
static int sgp_u_r_flush(queue_t * q, mblk_t * mp)
{
	return sgp_m_flush(q, mp, FLUSHR, FLUSHW);
}
static int sgp_l_w_flush(queue_t * q, mblk_t * mp)
{
	return sgp_m_flush(q, mp, FLUSHW, FLUSHR);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Other message Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sgp_u_w_other(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	ptrace(("Received unexpected message %ld on upper WR(q)\n", *((long *) mp->b_rptr)));
	return (-EOPNOTSUPP);
}
static int sgp_l_r_other(queue_t * q, mblk_t * mp)
{
	(void) q;
	(void) mp;
	ptrace(("Received unexpected message %ld on lower RD(q)\n", *((long *) mp->b_rptr)));
	return (-EOPNOTSUPP);
}
static int sgp_u_r_other(queue_t * q, mblk_t * mp)
{
	if (mp->b_datap->db_type >= QPCTL || canputnext(q))
		return (4);
	return (-EBUSY);
}
static int sgp_l_w_other(queue_t * q, mblk_t * mp)
{
	if (mp->b_datap->db_type >= QPCTL || canputnext(q))
		return (4);
	return (-EBUSY);
}

/*
 *  =========================================================================
 *
 *  STREAMS PUTQ and SRVQ routines
 *
 *  =========================================================================
 */
/*
 *  UA PUTQ
 *  -------------------------------------------------------------------------
 */
static int ua_putq(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *))
{
	int rtn;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (mp->b_datap->db_type < QPCTL && q->q_count) {
		seldom();
		putq(q, mp);
		return (0);
	}
	switch ((rtn = (*proc) (q, mp))) {
	case 0:
		freemsg(mp);
	case 1:
		break;
	case 2:
		freeb(mp);
		break;
	case 3:
		if (!q->q_next) {
			qreply(q, mp);
			break;
		}
	case 4:
		if (q->q_next) {
			putnext(q, mp);
			break;
		}
		rtn = -EOPNOTSUPP;
	default:
		ptrace(("Error (dropping) %d\n", rtn));
		freemsg(mp);
		return (rtn);
	case 5:
		if (canputnext(q)) {
			putnext(q, mp);
			break;
		}
	case -ENOBUFS:		/* caller must schedule bufcall */
	case -EBUSY:		/* caller must have failed canput */
	case -EAGAIN:		/* caller must re-enable queue */
	case -ENOMEM:		/* caller must re-enable queue */
		putq(q, mp);
		break;
	}
	return (0);
}

/*
 *  UA SRVQ
 *  -------------------------------------------------------------------------
 */
static int ua_srvq(queue_t * q, int (*proc) (queue_t *, mblk_t *))
{
	int rtn;
	mblk_t *mp;
	while ((mp = getq(q))) {
		switch ((rtn = (*proc) (q, mp))) {
		case 0:
			freemsg(mp);
		case 1:
			continue;
		case 2:
			freeb(mp);
			continue;
		case 3:
			if (!q->q_next) {
				qreply(q, mp);
				continue;
			}
		case 4:
			if (q->q_next) {
				putnext(q, mp);
				continue;
			}
			rtn = -EOPNOTSUPP;
		default:
			ptrace(("Error (dropping) %d\n", rtn));
			freemsg(mp);
			continue;
		case 5:
			if (canputnext(q)) {
				putnext(q, mp);
				continue;
			}
		case -ENOBUFS:	/* caller must schedule bufcall */
		case -EBUSY:	/* caller must have failed canput */
		case -EAGAIN:	/* caller must re-enable queue */
		case -ENOMEM:	/* caller must re-enable queue */
			if (mp->b_datap->db_type < QPCTL) {
				putb(q, mp);
				return (rtn);
			}
			ptrace(("Error (dropping) %d\n", rtn));
			freemsg(mp);
			continue;
		}
	}
	return (0);
}

/*
 *  UPPER WR and RD PUT and SRV
 *  -----------------------------------
 */
static int sgp_u_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sgp_u_w_data(q, mp);
	case M_PROTO:
		return sgp_u_w_proto(q, mp);
	case M_PCPROTO:
		return sgp_u_w_pcproto(q, mp);
	case M_IOCTL:
		return sgp_u_w_ioctl(q, mp);
	case M_IOCDATA:
		return sgp_u_w_iocdata(q, mp);
	case M_HANGUP:
		return sgp_u_w_hangup(q, mp);
	case M_FLUSH:
		return sgp_u_w_flush(q, mp);
	default:
		return sgp_u_w_other(q, mp);
	}
}
static INT sgp_u_wput(queue_t * q, mblk_t * mp)
{
	return ((INT) ua_putq(q, mp, &sgp_u_w_prim));
}
static INT sgp_u_wsrv(queue_t * q)
{
	return ((INT) ua_srvq(q, &sgp_u_w_prim));
}
static int sgp_u_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sgp_u_r_data(q, mp);
	case M_PROTO:
		return sgp_u_r_proto(q, mp);
	case M_PCPROTO:
		return sgp_u_r_pcproto(q, mp);
	case M_CTL:
		return sgp_u_r_ctl(q, mp);
	case M_HANGUP:
		return sgp_u_r_hangup(q, mp);
	case M_ERROR:
		return sgp_u_r_error(q, mp);
	case M_FLUSH:
		return sgp_u_r_flush(q, mp);
	default:
		return sgp_u_r_other(q, mp);
	}
}
static INT sgp_u_rput(queue_t * q, mblk_t * mp)
{
	return ((INT) ua_putq(q, mp, &sgp_u_r_prim));
}
static INT sgp_u_rsrv(queue_t * q)
{
	return ((INT) ua_srvq(q, &sgp_u_r_prim));
}

/*
 *  LOWER WR and RD PUT and SRV
 *  -----------------------------------
 */
static int sgp_l_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sgp_l_w_data(q, mp);
	case M_PROTO:
		return sgp_l_w_proto(q, mp);
	case M_PCPROTO:
		return sgp_l_w_pcproto(q, mp);
	case M_HANGUP:
		return sgp_l_w_hangup(q, mp);
	case M_FLUSH:
		return sgp_l_w_flush(q, mp);
	default:
		return sgp_l_w_other(q, mp);
	}
}
static INT sgp_l_wput(queue_t * q, mblk_t * mp)
{
	return ((INT) ua_putq(q, mp, &sgp_l_w_prim));
}
static INT sgp_l_wsrv(queue_t * q)
{
	return ((INT) ua_srvq(q, &sgp_l_w_prim));
}
static int sgp_l_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sgp_l_r_data(q, mp);
	case M_PROTO:
		return sgp_l_r_proto(q, mp);
	case M_PCPROTO:
		return sgp_l_r_pcproto(q, mp);
	case M_CTL:
		return sgp_l_r_ctl(q, mp);
	case M_HANGUP:
		return sgp_l_r_hangup(q, mp);
	case M_ERROR:
		return sgp_l_r_error(q, mp);
	case M_FLUSH:
		return sgp_l_r_flush(q, mp);
	default:
		return sgp_l_r_other(q, mp);
	}
}
static INT sgp_l_rput(queue_t * q, mblk_t * mp)
{
	return ((INT) ua_putq(q, mp, &sgp_l_r_prim));
}
static INT sgp_l_rsrv(queue_t * q)
{
	return ((INT) ua_srvq(q, &sgp_l_r_prim));
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 */
static ua_t *sgp_opens_list = NULL;

static int sgp_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	dp_t *dp, **dpp;
	if (q->q_ptr != NULL)
		return (0);	/* already open */
	if (sflag == MODOPEN || WR(q)->q_next)
		return (EIO);	/* can't open as module */
	if (!cminor)
		sflag = CLONEOPEN;
	if (sflag == CLONEOPEN)
		cminor = 1;
	dpp = &sgp_opens_list;
	for (; *dpp && getmajor((*dpp)->id.dev) < cmajor; dpp = &(*dpp)->next);
	for (; *dpp && cminor <= ASP_NMINOR; dpp = &(*dpp)->next) {
		ushort dminor = getminor((*dpp)->id.dev);
		if (cminor < dminor)
			break;
		if (cminor == dminor) {
			if (sflag == CLONEOPEN) {
				cminor++;
				continue;
			}
			return (EIO);	/* requested device in use */
		}
	}
	if (cminor > ASP_NMINOR)
		return (ENXIO);
	*devp = makedevice(cmajor, minor);
	if (!(dp = kmem_cache_alloc(ua_pp_cachep, SLAB_ATOMIC)))
		return (ENOMEM);
	bzero(dp, sizeof(ua_t));
	if ((dp->next = *dpp))
		dp->next->prev = &dp->next;
	dp->prev = dpp;
	*dpp = dp;
	dp->id.dev = *devp;
	dp->rq = RD(q);
	dp->wq = WR(q);
	dp->mq = RD(q);
	dp->rq->q_ptr = dp->wq->q_ptr = dp;
	return (0);
}
static int sgp_close(queue_t * q, int flag, cred_t * crp)
{
	dp_t *dp = (dp_t *) q->q_ptr;
	if ((*(dp->prev) = dp->next))
		dp->next->prev = dp->prev;
	dp->prev = NULL;
	dp->next = NULL;
	if (dp->bid)
		unbufcall(dp->bid);
	dp->rq->q_ptr = NULL;
	dp->wq->q_ptr = NULL;
	kmem_cache_free(ua_pp_cachep, dp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
int sgp_init(void)
{
	int cmajor;
	if (!sgp_u_minfo.mi_idnum) {
		if ((cmajor = lis_regsiter_strdev(ASP_CMAJOR, &sgp_info, ASP_NMINOR,
						  sgp_u_minfo.mi_idname)) < 0) {
			sgp_u_minfo.mi_idnum = 0;
			rare();
			cnm_err(CE_NOTE, "sgp: couldn't register driver\n");
			return;
		}
		sgp_u_minfo.mi_idnum = cmajor;
	}
	return (0);
}
void sgp_terminate(void)
{
	if (sgp_u_minfo.mi_idnum) {
		if ((sgp_u_minfo.mi_idnum = lis_unregister_strdev(sgp_u_minfo.mi_idnum))) {
			sgp_u_minfo.mi_idnum = 0;
			rare();
			cmn_err(CE_WARN, "sgp: couldn't unregister driver!\n");
		}
	}
}

/*
 *  =========================================================================
 *
 *  Kernel Module Initialization
 *
 *  =========================================================================
 */
int init_module(void)
{
	int err;
	sgp_init();
	return (0);
}
void cleanup_module(void)
{
	sgp_terminate();
	return;
}


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

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

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