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/m2ua2/ua_asp.c


File /code/strss7/drivers/m2ua2/ua_asp.c



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

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

#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 "asp_data.h"
#include "asp_msg.h"

#define ASP_DESCRIP	"UA/ASP STREAMS MULTIPLEXING DRIVER."
#define ASP_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define ASP_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define ASP_CONTACT	"Brian Bidulock <bidulock@openss7.org>
#define ASP_LICENSE	"GPL"
#define ASP_BANNER	ASP_DESCRIP	"\n" \
			ASP_COPYRIGHT	"\n" \
			ASP_DEVICE	"\n" \
			ASP_CONTACT	"\n"

MODULE_AUTHOR(ASP_CONTACT);
MODULE_DESCRIPTION(ASP_DESCRIP);
MODULE_SUPPORTED_DEVICE(ASP_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(ASP_LICENSE);
#endif

#ifndef INT
#define INT void
#endif

/*
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */
static struct module_info asp_u_minfo =
{
	0,		/* Module ID number		*/
	"asp-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 asp_l_minfo =
{
	0,		/* Module ID number		*/
	"asp-n",	/* Module name			*/
	0,		/* Min packet size accepted	*/
	INFPSZ,		/* Max packet size accepted	*/
	1,		/* Hi water mark		*/
	0		/* Lo water mark		*/
};

static int asp_open (queue_t *, dev_t *, int, int, cred_t *);
static int asp_close(queue_t *, int, cred_t *);

static INT asp_u_wput(queue_t *, mblk_t *);
static INT asp_u_wsrv(queue_t *);

static struct qinit asp_u_winit =
{
	asp_u_wput,	/* Write put (msg from above)	*/
	asp_u_wsrv,	/* Write queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&asp_u_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static INT asp_u_rput(queue_t *, mblk_t *);
static INT asp_u_rsrv(queue_t *);

static struct qinit asp_u_rinit =
{
	asp_u_rput,	/* Read put (msg from below)	*/
	asp_u_rsrv,	/* Read queue service		*/
	asp_open,	/* Each open			*/
	asp_close,	/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&asp_u_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static INT asp_l_wput(queue_t *, mblk_t *);
static INT asp_l_wsrv(queue_t *);

static struct qinit asp_l_winit =
{
	asp_l_rput,	/* Write put (msg from above)	*/
	asp_l_rsrv,	/* Write queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&asp_l_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static INT asp_l_rput(queue_t *, mblk_t *);
static INT asp_l_rsrv(queue_t *);

static struct qinit asp_l_rinit =
{
	asp_l_rput,	/* Read put (msg from below)	*/
	asp_l_rsrv,	/* Read queue service		*/
	NULL,		/* Each open			*/
	NULL,		/* Last close			*/
	NULL,		/* Admin (not used)		*/
	&asp_l_minfo,	/* Information			*/
	NULL		/* Statistics			*/
};

static struct streamtab asp_info =
{
	&asp_u_rinit,	/* Upper read  queue		*/
	&asp_u_winit,	/* Upper write queue		*/
	&asp_l_rinit,	/* Lower read  queue		*/
	&asp_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);
}
/*
 *  ASP 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);
}

/*
 *  ASP 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);
}

/*
 *  ASP message written for lower stream (need translating).
 */
static int asp_mgmt_w_err(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_mgmt_w_ntfy(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_snmm_w_duna(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_snmm_w_dava(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_snmm_w_daud(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_snmm_w_scon(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_snmm_w_dupu(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_snmm_w_drst(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_asps_w_aspup_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_asps_w_aspdn_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_asps_w_hbeat_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_asps_w_aspup_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_asps_w_aspdn_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_asps_w_hbeat_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_aspt_w_aspac_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_aspt_w_aspia_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_aspt_w_aspac_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_aspt_w_aspia_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_w_reg_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_w_reg_rsp(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_w_dereg_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_w_dereg_rsp(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}

/*
 *  =========================================================================
 *
 *  ASP State Machine
 *
 *  =========================================================================
 */
/*
 *  =========================================================================
 *
 *  UA Peer --> UA (ASP) Received Messages
 *
 *  =========================================================================
 *
 *  These are procedures for common UA received messages at the ASP.
 */
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 ASP 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
			 *  ASP 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
 *
 *  =========================================================================
 */

/*
 *  ASP message read for upper stream (need translating).
 */
static int asp_mgmt_r_err(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_mgmt_r_ntfy(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_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 asp_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 asp_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;
	asp_t *asp = 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 asp_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;
	asp_t *asp = ua->u.spp;
	if ( asp && asp->state == ASP_WACK_ASPUP )
	{
		fixme(("We should also check that the ASPId is valid\n"));
		as->state = ASP_INACTIVE;
		if ( (err = asp_recalc_as_state(as)) )
		{
			as->state = ASP_WACK_ASPUP;
			return(err);
		}
		fixme(("Notify LM of ASP 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 asp_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;
	asp_t *asp = ua->u.spp;
	if ( asp && asp->state == ASP_WACK_ASPDN )
	{
		asp->state = ASP_DOWN;
		if ( (err = asp_recalc_as_state(asp)) )
		{
			asp->state = ASP_WACK_ASPDN;
			return(err);
		}
		fixme(("Notify LM of ASP 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 asp_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;
	asp_t *asp = 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 ( asp && asp->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 asp_aspt_r_aspac_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_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 asp_aspt_r_aspac_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
	asp_t *asp = 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 ASP Active Ack.  Nowhere in any of the UA drafts
	 *  does it say what this parameter is supposed to contain nor what
	 *  the ASP 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 ASP 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 = asp->gp; gp && gp->sp->rc == ; gp = gp->sp_next )
			{
		}
	}
	else
	{
		/*
		 *  Walk through all the AS assigned to the ASP and perform
		 *  the function for the associated AS.
		 */
		gp_t *gp;
		for ( gp = asp->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 ASP 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 ( asp ) {
		switch ( asp->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 asp_aspt_r_aspia_ack(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_r_reg_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_r_reg_rsp(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_r_dereg_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int asp_rkmm_r_dereg_rsp(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}

static int asp_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;
	}
}

/*
 *  ASP 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;
}
static int lm_as_del_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_proc_add_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_proc_del_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_link_add_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_link_del_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_route_add_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_route_del_req(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}
static int lm_reg_res(queue_t *q, mblk_t *mp)
{
	int err;
	ua_t *ua = (ua_t *)q->q_ptr;
}

/*
 *  ASP 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 = asp_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 = asp_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 asp_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(("ASP 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(("ASP 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(("ASP 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(("ASP 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(("ASP 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(("ASP 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(("ASP 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(("ASP 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(("ASP 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(("ASP received N_RESET_CON on lower RD(q) %d\n",ua->id.mux));
	return(-EFAULT);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCDATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_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 *asp_links_list = NULL;

static int asp_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(asp_cachep, SLAB_ATOMIC)) )
					{
						bzero(lp, sizeof(lp_t));
						if ( (lp->next = asp_links_list) )
							lp->next->prev = &lp->next;
						lp->prev = &asp_links_list;
						asp_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(asp_cachep, lp);
					qenable(lp->rq);
					qenable(lp->wq);
					break;
			}
		}
/*		case SL_IOC_MAGIC:	*/  /* can't control these thru ASP */
/*		case SDT_IOC_MAGIC:	*/  /* can't control these thru ASP */
/*		case SDL_IOC_MAGIC:	*/  /* can't control these thru ASP */
/*		case DEV_IOC_MAGIC:	*/  /* can't control these thru ASP */
	}
	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 asp_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 asp_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 asp_u_r_proto(queue_t *q, mblk_t *mp)
{
	return(5);
}
static int asp_l_w_proto(queue_t *q, mblk_t *mp)
{
	return(5);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_u_w_pcproto(queue_t *q, mblk_t *mp)
{
	return asp_u_w_proto(q, mp);
}
static int asp_l_r_pcproto(queue_t *q, mblk_t *mp)
{
	return asp_l_r_proto(q, mp);
}
static int asp_u_r_pcproto(queue_t *q, mblk_t *mp)
{
	return(4);
}
static int asp_l_w_pcproto(queue_t *q, mblk_t *mp)
{
	return(4);
}
/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_u_w_data(queue_t *q, mblk_t *dp)
{
	(void)q; (void)dp;
	ptrace(("ASP 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 asp_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 asp_u_r_data(queue_t *q, mblk_t *mp)
{
	return(5);
}
static int asp_l_w_data(queue_t *q, mblk_t *mp)
{
	return(5);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_CTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_u_w_ctl(queue_t *q, mblk_t *mp)
{
	(void)q; (void)mp;
	rare(); return(-EOPNOTSUPP);
}
static int asp_l_r_ctl(queue_t *q, mblk_t *mp)
{
	(void)q; (void)mp;
	rare(); return(-EOPNOTSUPP);
}
static int asp_u_r_ctl(queue_t *q, mblk_t *mp)
{
	return asp_u_r_data(q, mp);
}
static int asp_l_w_ctl(queue_t *q, mblk_t *mp)
{
	return asp_l_w_data(q, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_HANGUP Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_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 asp_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 asp_u_r_hangup(queue_t *q, mblk_t *mp)
{
	return(4);
}
static int asp_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 asp_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 asp_u_r_error(queue_t *q, mblk_t *mp)
{
	return(4);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_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 asp_u_w_flush(queue_t *q, mblk_t *mp)
{
	return asp_m_flush(q, mp, FLUSHW, FLUSHR);
}
static int asp_l_r_flush(queue_t *q, mblk_t *mp)
{
	return asp_m_flush(q, mp, FLUSHR, FLUSHW);
}
static int asp_u_r_flush(queue_t *q, mblk_t *mp)
{
	return asp_m_flush(q, mp, FLUSHR, FLUSHW);
}
static int asp_l_w_flush(queue_t *q, mblk_t *mp)
{
	return asp_m_flush(q, mp, FLUSHW, FLUSHR);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Other message Handling
 *
 *  -------------------------------------------------------------------------
 */
static int asp_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 asp_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 asp_u_r_other(queue_t *q, mblk_t *mp)
{
	if ( mp->b_datap->db_type >= QPCTL || canputnext(q) )
		return(4);
	return(-EBUSY);
}
static int asp_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 asp_u_w_prim(queue_t *q, mblk_t *mp)
{
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	return asp_u_w_data	(q, mp);
		case M_PROTO:	return asp_u_w_proto	(q, mp);
		case M_PCPROTO:	return asp_u_w_pcproto	(q, mp);
		case M_IOCTL:	return asp_u_w_ioctl	(q, mp);
		case M_IOCDATA:	return asp_u_w_iocdata	(q, mp);
		case M_HANGUP:	return asp_u_w_hangup	(q, mp);
		case M_FLUSH:	return asp_u_w_flush	(q, mp);
		default:	return asp_u_w_other	(q, mp);
	}
}
static INT asp_u_wput(queue_t *q, mblk_t *mp)
{
	return((INT)ua_putq(q, mp, &asp_u_w_prim));
}
static INT asp_u_wsrv(queue_t *q)
{
	return((INT)ua_srvq(q, &asp_u_w_prim));
}
static int asp_u_r_prim(queue_t *q, mblk_t *mp)
{
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	return asp_u_r_data	(q, mp);
		case M_PROTO:	return asp_u_r_proto	(q, mp);
		case M_PCPROTO:	return asp_u_r_pcproto	(q, mp);
		case M_CTL:	return asp_u_r_ctl	(q, mp);
		case M_HANGUP:	return asp_u_r_hangup	(q, mp);
		case M_ERROR:	return asp_u_r_error	(q, mp);
		case M_FLUSH:	return asp_u_r_flush	(q, mp);
		default:	return asp_u_r_other	(q, mp);
	}
}
static INT asp_u_rput(queue_t *q, mblk_t *mp)
{
	return((INT)ua_putq(q, mp, &asp_u_r_prim));
}
static INT asp_u_rsrv(queue_t *q)
{
	return((INT)ua_srvq(q, &asp_u_r_prim));
}
/*
 *  LOWER WR and RD PUT and SRV
 *  -----------------------------------
 */
static int asp_l_w_prim(queue_t *q, mblk_t *mp)
{
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	return asp_l_w_data	(q, mp);
		case M_PROTO:	return asp_l_w_proto	(q, mp);
		case M_PCPROTO:	return asp_l_w_pcproto	(q, mp);
		case M_HANGUP:	return asp_l_w_hangup	(q, mp);
		case M_FLUSH:	return asp_l_w_flush	(q, mp);
		default:	return asp_l_w_other	(q, mp);
	}
}
static INT asp_l_wput(queue_t *q, mblk_t *mp)
{
	return((INT)ua_putq(q, mp, &asp_l_w_prim));
}
static INT asp_l_wsrv(queue_t *q)
{
	return((INT)ua_srvq(q, &asp_l_w_prim));
}
static int asp_l_r_prim(queue_t *q, mblk_t *mp)
{
	switch ( mp->b_datap->db_type )
	{
		case M_DATA:	return asp_l_r_data	(q, mp);
		case M_PROTO:	return asp_l_r_proto	(q, mp);
		case M_PCPROTO:	return asp_l_r_pcproto	(q, mp);
		case M_CTL:	return asp_l_r_ctl	(q, mp);
		case M_HANGUP:	return asp_l_r_hangup	(q, mp);
		case M_ERROR:	return asp_l_r_error	(q, mp);
		case M_FLUSH:	return asp_l_r_flush	(q, mp);
		default:	return asp_l_r_other	(q, mp);
	}
}
static INT asp_l_rput(queue_t *q, mblk_t *mp)
{
	return((INT)ua_putq(q, mp, &asp_l_r_prim));
}
static INT asp_l_rsrv(queue_t *q)
{
	return((INT)ua_srvq(q, &asp_l_r_prim));
}

/*
 *  =========================================================================
 *
 *  Private Structure cache
 *
 *  =========================================================================
 */
kmem_cache_t *asp_cachep = NULL;
static int asp_init_caches(void)
{
	if (!(asp_cachep) &&
	    !(asp_cachep = kmem_cache_create
	      ("asp_cachep", sizeof(ua_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL)))
		return (ENOMEM);
	return;
}
static void asp_term_caches(void)
{
	if (asp_cachep)
		if (kmem_cache_destroy(asp_cachep))
			cmn_err(CE_WARN, "ua-asp: did not destroy asp_cachep");
	return;
}

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

static int asp_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 = &asp_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(asp_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 asp_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->rq->q_ptr = NULL;
	kmem_cache_free(asp_cachep, dp);
	return(0);
}

/*
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
int asp_init(void)
{
	int err;
	int cmajor;
	if ( !asp_u_minfo.mi_idnum )
	{
		if ( (err = asp_init_caches()) )
			return(err);
		if ( (cmajor = lis_regsiter_strdev(ASP_CMAJOR, &asp_info, ASP_NMINOR,
						asp_u_minfo.mi_idname)) < 0 )
		{
			asp_u_minfo.mi_idnum = 0;
			rare();
			cnm_err(CE_NOTE, "asp: couldn't register driver\n");
			return;
		}
		asp_u_minfo.mi_idnum = cmajor;
	}
	return(0);
}
void asp_terminate(void)
{
	if ( asp_u_minfo.mi_idnum )
	{
		asp_term_caches();
		if ( (asp_u_minfo.mi_idnum = lis_unregister_strdev(asp_u_minfo.mi_idnum)) )
		{
			asp_u_minfo.mi_idnum = 0;
			rare();
			cmn_err(CE_WARN, "asp: couldn't unregister driver!\n");
		}
	}
}

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


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

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

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