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/tcap/tcap2.c


File /code/strss7/drivers/tcap/tcap2.c



#ident "@(#) $RCSfile: tcap2.c,v $ $Name:  $($Revision: 0.8 $) $Date: 2003/04/30 13:09:10 $"

static char const ident[] = "$RCSfile: tcap2.c,v $ $Name:  $($Revision: 0.8 $) $Date: 2003/04/30 13:09:10 $ Copyright (c) 1997-2003 OpenSS7 Corporation.";

/*
 *  This is a TCAP (Transaction Capabilities Application Part) multiplexing
 *  driver whcih can have SCCP (Signalling Connection Control Part) or any
 *  other NPI conforming (e.g., X.25 NSP) stream I_LINK'ed or I_PLINK'ed
 *  underneath it to form a complete Transaction Capabilities Application Part
 *  protocol layer for SS7.
 */

#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 <ss7/lmi.h>
#include <ss7/lmi_ioctl.h>
#include <ss7/mtpi.h>
#include <ss7/mtpi_ioctl.h>
#include <ss7/sccpi.h>
#include <ss7/sccpi_ioctl.h>

#include <sys/npi.h>
#include <sys/npi_ss7.h>
#include <sys/npi_mtp.h>
#include <sys/npi_sccp.h>
#include <sys/tpi.h>
#include <sys/tpi_ss7.h>
#include <sys/tpi_mtp.h>
#include <sys/tpi_sccp.h>
#include <sys/tpi_tr.h>
#include <sys/tpi_tc.h>
#include <sys/xti_mtp.h>
#include <sys/xti_sccp.h>
#include <sys/xti_tr.h>
#include <sys/xti_tc.h>

#include "../debug.h"
#include "../bufq.h"
#include "../priv.h"
#include "../lock.h"
#include "../queue.h"
#include "../allocb.h"
#include "../timer.h"

#define TCAP_DESCRIP	"SS7 TRANSACTION CAPABILITIES APPLICATION PART (TCAP) STREAMS MULTIPLEXING DRIVER."
#define TCAP_COPYRIGHT	"Copyright (c) 1997-2003 OpenSS7 Corporation.  All Rights Reserved."
#define TCAP_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define TCAP_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define TCAP_LICENSE	"GPL"
#define TCAP_BANNER	TCAP_DESCRIP	"\n" \
			TCAP_COPYRIGHT	"\n" \
			TCAP_DEVICE	"\n" \
			TCAP_CONTACT

MODULE_AUTHOR(TCAP_CONTACT);
MODULE_DESCRIPTION(TCAP_DESCRIP);
MODULE_SUPPORTED_DEVICE(TCAP_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(TCAP_LICENSE);
#endif

#ifndef TCAP_CMAJOR
#error "TCAP_CMAJOR must be defined\n"
#endif
#define TCAP_NMAJOR 1
#define TCAP_NMINOR 255

#define TCAP_STYLE_TRI	TCAP_CMINOR_TRI
#define TCAP_STYLE_TCI	TCAP_CMINOR_TCI
#define TCAP_STYLE_TPI	TCAP_CMINOR_TPI
#define TCAP_STYLE_MGMT	TCAP_CMINOR_MGMT

/* 
 *  =========================================================================
 *
 *  STREAMS Definitions
 *
 *  =========================================================================
 */

#define TCAP_DRV_ID	TCAP_IOC_MAGIC
#define TCAP_DRV_NAME	"tcap"

STATIC struct module_info tcap_winfo = {
	mi_idnum:TCAP_DRV_ID,			/* Module ID number */
	mi_idname:TCAP_DRV_NAME "-wr",		/* Module ID name */
	mi_minpsz:1,				/* Min packet size accepted */
	mi_maxpsz:272 + 1,			/* Max packet size accepted */
	mi_hiwat:1 << 15,			/* Hi water mark */
	mi_lowat:1 << 10,			/* Lo water mark */
};
STATIC struct module_info tcap_rinfo = {
	mi_idnum:TCAP_DRV_ID,			/* Module ID number */
	mi_idname:TCAP_DRV_NAME "-rd",		/* Module ID name */
	mi_minpsz:1,				/* Min packet size accepted */
	mi_maxpsz:272 + 1,			/* Max packet size accepted */
	mi_hiwat:1 << 15,			/* Hi water mark */
	mi_lowat:1 << 10,			/* Lo water mark */
};
STATIC struct module_info sccp_winfo = {
	mi_idnum:TCAP_DRV_ID,			/* Module ID number */
	mi_idname:TCAP_DRV_NAME "-muxw",	/* Module ID name */
	mi_minpsz:1,				/* Min packet size accepted */
	mi_maxpsz:272 + 1,			/* Max packet size accepted */
	mi_hiwat:1 << 15,			/* Hi water mark */
	mi_lowat:1 << 10,			/* Lo water mark */
};
STATIC struct module_info sccp_rinfo = {
	mi_idnum:TCAP_DRV_ID,			/* Module ID number */
	mi_idname:TCAP_DRV_NAME "-muxr",	/* Module ID name */
	mi_minpsz:1,				/* Min packet size accepted */
	mi_maxpsz:272 + 1,			/* Max packet size accepted */
	mi_hiwat:1 << 15,			/* Hi water mark */
	mi_lowat:1 << 10,			/* Lo water mark */
};

STATIC int tcap_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int tcap_close(queue_t *, int, cred_t *);

STATIC struct qinit tcap_rinit = {
	qi_putp:ss7_oput,			/* Read put (message from below) */
	qi_srvp:ss7_osrv,			/* Read queue service */
	qi_qopen:tcap_open,			/* Each open */
	qi_qclose:tcap_close,			/* Last close */
	qi_minfo:&tcap_rinfo,			/* Information */
};
STATIC struct qinit tcap_winit = {
	qi_putp:ss7_iput,			/* Write put (message from above) */
	qi_srvp:ss7_isrv,			/* Write queue service */
	qi_minfo:&tcap_winfo,			/* Information */
};
STATIC struct qinit sccp_rinit = {
	qi_putp:ss7_iput,			/* Read put (message from below) */
	qi_srvp:ss7_isrv,			/* Read queue service */
	qi_minfo:&sccp_rinfo,			/* Information */
};
STATIC struct qinit sccp_winit = {
	qi_putp:ss7_oput,			/* Write put (message from above) */
	qi_srvp:ss7_osrv,			/* Write queue service */
	qi_minfo:&sccp_winfo,			/* Information */
};

STATIC struct streamtab tcap_info = {
	st_rdinit:&tcap_rinit,			/* Upper read queue */
	st_wrinit:&tcap_winit,			/* Upper write queue */
	st_muxrinit:&sccp_rinit,		/* Lower read queue */
	st_muxwinit:&sccp_winit,		/* Lower write queue */
};

/* 
 *  =========================================================================
 *
 *  Private Structures
 *
 *  =========================================================================
 */

#define TCAP_BUF_MAXLEN	    32
typedef struct tcap_buf {
	uchar buf[TCAP_BUF_MAXLEN];
	size_t len;
} tcap_buf_t;

typedef struct tcap_options {
	tcap_buf_t ctx;				/* application context */
	tcap_buf_t inf;				/* user information */
	t_uscalar_t pcl;			/* protocol class */
	t_uscalar_t ret;			/* return option */
	t_uscalar_t imp;			/* importance */
	t_uscalar_t seq;			/* sequence control */
	t_uscalar_t pri;			/* priority */
	t_uscalar_t sls;			/* signalling link selection */
	t_uscalar_t mp;				/* message priority */
} tcap_options_t;

struct tcap_options opt_defaults;

struct tcap;					/* TCAP user */
struct iv;					/* invoke */
struct dg;					/* dialog */
struct tr;					/* transaction */
struct sp;					/* SCCP-SAP */
struct sccp;					/* SCCP provider */

/*
 *  Trasnaction Capabilities Application Part
 *  -----------------------------------------------------------
 */
typedef struct tcap {
	STR_DECLARATION (struct tcap);		/* stream declaration */
	SLIST_HEAD (tr, tr);			/* transaction list */
	SLIST_HEAD (dg, dg);			/* dialogue list */
	ulong token;				/* token for this stream */
	ulong conind;				/* max connection inds */
	ulong outcnt;				/* out connection inds */
	ulong pclass;				/* protocol class */
	ulong reterr;				/* return on error */
	struct sccp_addr src;			/* bound src address */
	uint8_t saddr[SCCP_MAX_ADDR_LENGTH];
	struct sccp_addr dst;			/* bound dst address */
	uint8_t daddr[SCCP_MAX_ADDR_LENGTH];
	bufq_t conq;				/* connection indication queue */
	struct tcap_options options;		/* settable options */
	struct lmi_option option;		/* tcap protocol variant and options */
	struct tcap_notify_tc notify;		/* tcap notifications */
	struct tcap_timers_tc timers;		/* tcap timers */
	struct tcap_opt_conf_tc config;		/* tcap configuration */
	struct tcap_stats_tc statsp;		/* tcap statistics periods */
	struct tcap_stats_tc stamp;		/* tcap statistics timestamps */
	struct tcap_stats_tc stats;		/* tcap statistics */
} tcap_t;

STATIC struct tcap *tcap_alloc_priv(queue_t *, struct tcap **, dev_t *, cred_t *, ushort);
STATIC struct tcap *tcap_get(struct tcap *);
STATIC struct tcap *tcap_lookup(ulong);
STATIC ulong tcap_get_id(ulong);
STATIC void tcap_free_priv(struct tcap *);
STATIC void tcap_put(struct tcap *);

/*
 *  Invoke
 *  -----------------------------------------------------------
 */
typedef struct iv {
	HEAD_DECLARATION (struct iv);		/* head declaration */
	SLIST_LINKAGE (dg, iv, dg);		/* associated dialogue */
	ulong oper;				/* operation */
	ulong iid;				/* invoke id */
	ulong lid;				/* linked id */
	struct tcap_notify_iv notify;		/* invoke notifications */
	struct tcap_timers_iv timers;		/* invoke protocol timers */
	struct tcap_opt_conf_iv config;		/* invoke configuration */
	struct tcap_stats_iv statsp;		/* invoke statistics periods */
	struct tcap_stats_iv stamp;		/* invoke statsistics timestamps */
	struct tcap_stats_iv stats;		/* invoke statistics */
} iv_t;

STATIC struct iv *tcap_alloc_iv(ulong);
STATIC struct iv *iv_get(struct iv *);
STATIC struct iv *iv_lookup(struct dg *, ulong);
STATIC ulong iv_get_id(ulong);
STATIC void tcap_free_iv(struct iv *);
STATIC void iv_put(struct iv *);

/*
 *  Dialog
 *  -----------------------------------------------------------
 */
typedef struct dg {
	HEAD_DECLARATION (struct dg);		/* head declaration */
	SLIST_LINKAGE (tr, dg, tr);		/* associated transaction */
	SLIST_HEAD (iv, dg, iv);		/* list of invocations */
	ulong did;				/* dialogue id */
	struct tcap_notify_dg notify;		/* dialog notifications */
	struct tcap_timers_dg timers;		/* dialog protocol timers */
	struct tcap_opt_conf_dg config;		/* dialog configuration */
	struct tcap_stats_dg statsp;		/* dialog statistics periods */
	struct tcap_stats_dg stamp;		/* dialog statsistics timestamps */
	struct tcap_stats_dg stats;		/* dialog statistics */
} dg_t;

STATIC struct dg *tcap_alloc_dg(ulong);
STATIC struct dg *dg_get(struct dg *);
STATIC struct dg *dg_lookup(struct tr *, ulong);
STATIC ulong dg_get_id(ulong);
STATIC void tcap_free_dg(struct dg *);
STATIC void dg_put(struct dg *);

/*
 *  Transaction
 *  -----------------------------------------------------------
 */
typedef struct tr {
	HEAD_DECLARATION (struct tr);		/* head declaration */
	SLIST_LINKAGE (tcap, tr, tcap);		/* associated TCAP user */
	SLIST_LINKAGE (sp, tr, sp);		/* associated SCCP-SAP */
	SLIST_HEAD (iv, iv);			/* invokes for this transaction */
	ulong cid;				/* correlation id */
	ulong tid;				/* transaction id */
	ulong ocls;				/* operation class */
	struct tcap_notify_tr notify;		/* transaction notifications */
	struct tcap_timers_tr timers;		/* transaction protocol timers */
	struct tcap_opt_conf_tr config;		/* transaction configuration */
	struct tcap_stats_tr statsp;		/* transaction statistics periods */
	struct tcap_stats_tr stamp;		/* transaction statsistics timestamps */
	struct tcap_stats_tr stats;		/* transaction statistics */
} tr_t;

STATIC struct tr *tcap_alloc_tr(struct tcap *, ulong, ulong);
STATIC struct tr *tr_get(struct tr *);
STATIC struct tr *tr_lookup_cid(struct tcap *, ulong);
STATIC struct tr *tr_lookup_tid(struct tcap *, ulong);
STATIC ulong tr_get_id(ulong);
STATIC void tcap_free_tr(struct tr *);
STATIC void tr_put(struct tr *);

/*
 *  SCCP SAP
 *  -----------------------------------------------------------
 */
typedef struct sp {
	HEAD_DECLARATION (struct sp);		/* head declaration */
	struct sccp *sccp;			/* sccp for this sp */
	SLIST_HEAD (tr, tr);			/* transaction list for this sp */
	struct tcap_notify_sp notify;		/* SCCP-SAP notifications */
	struct tcap_timers_sp timers;		/* SCCP-SAP protocol timers */
	struct tcap_opt_conf_sp config;		/* SCCP-SAP configuration */
	struct tcap_stats_sp statsp;		/* SCCP-SAP statistics periods */
	struct tcap_stats_sp stamp;		/* SCCP-SAP statistics timestamps */
	struct tcap_stats_sp stats;		/* SCCP-SAP statistics */
} sp_t;

STATIC struct sp *tcap_alloc_sp(ulong);
STATIC struct sp *sp_get(struct sp *);
STATIC struct sp *sp_lookup(ulong);
STATIC ulong sp_get_id(ulong);
STATIC void tcap_free_sp(struct sp *);
STATIC void sp_put(struct sp *);

/*
 *  SCCP
 *  -----------------------------------------------------------
 */
typedef struct sccp {
	STR_DECLARATION (struct sccp);		/* stream declaration */
	struct sp *sp;				/* SCCP-SAP instance for this stream */
	ulong psdu;				/* pSDU size */
	ulong pidu;				/* pIDU size */
	ulong podu;				/* pODU size */
	struct sccp_addr add;			/* local address */
	uint8_t addr[SCCP_MAX_ADDR_LENGTH];
	struct lmi_option option;		/* protocol variant and options */
	struct tcap_notify_sc notify;		/* tcap notifications */
	struct tcap_timers_sc timers;		/* tcap protocol timers */
	struct tcap_opt_conf_sc config;		/* tcap configuration */
	struct tcap_stats_sc statsp;		/* tcap statistics periods */
	struct tcap_stats_sc stamp;		/* tcap statistics timestamp */
	struct tcap_stats_sc stats;		/* tcap statistics */
} sccp_t;
#define SCCP_PRIV(__q) ((struct sccp *)(__q)->q_ptr)

STATIC struct sccp *tcap_alloc_link(ulong);
STATIC struct sccp *sccp_get(struct sccp *);
STATIC struct sccp *sccp_lookup(ulong);
STATIC ulong sccp_get_id(ulong);
STATIC void sccp_free_link(struct sccp *);
STATIC void sccp_put(struct sccp *);

/*
 *  Default
 *  -----------------------------------------------------------
 */
typedef struct df {
	lis_spin_lock_t lock;
	SLIST_HEAD (tcap, tcap);		/* master list of TCAP users */
	SLIST_HEAD (tr, tr);			/* master list of transactions */
	SLIST_HEAD (sp, sp);			/* master list of SCCP-SAPs */
	SLIST_HEAD (sccp, sccp);		/* master list of SCCP providers */
	struct lmi_option proto;		/* default protocol variant and options */
	struct tcap_notify_df notify;		/* default notifications */
	struct tcap_timers_df timers;		/* default protocol timers */
	struct tcap_opt_conf_df config;		/* default configuration */
	struct tcap_stats_df statsp;		/* default statistics periods */
	struct tcap_stats_df stamp;		/* default statistics timestamps */
	struct tcap_stats_df stats;		/* default statistics */
} df_t;
STATIC struct df master;

/*
 *  =========================================================================
 *
 *  TCAP Message Structures
 *
 *  =========================================================================
 */

/* Message Types */
#define	TCAP_MT_UNI		1	/* Unidirectional */
#define	TCAP_MT_QWP		2	/* Query w/ permission */
#define	TCAP_MT_QWOP		3	/* Query w/o permission */
#define	TCAP_MT_CWP		4	/* Conversation w/ permission */
#define	TCAP_MT_CWOP		5	/* Conversation w/o permission */
#define	TCAP_MT_RESP		6	/* Response */
#define	TCAP_MT_ABORT		7	/* Abort */

/* Component Types */
#define	TCAP_CT_INVOKE_L	1	/* Invoke (Last) */
#define	TCAP_CT_INVOKE_NL	2	/* Invoke (Not Last) */
#define	TCAP_CT_RESULT_L	3	/* Return Result (Last) */
#define	TCAP_CT_RESULT_NL	4	/* Return Result (Not Last) */
#define	TCAP_CT_REJECT		5	/* Reject */
#define	TCAP_CT_ERROR		6	/* Error */

#define TCAP_TAG_UNIV_INT	2	/* UNIV Integer */
#define TCAP_TAG_UNIV_OSTR	4	/* UNIV Octet String */
#define TCAP_TAG_UNIV_OID	6	/* UNIV Object Id */
#define	TCAP_TAG_UNIV_PSEQ	16	/* UNIV Parameter Sequence */
#define TCAP_TAG_UNIV_UTC	17	/* UNIV Universal Time */
#define TCAP_TAG_UNIV_SEQ	48	/* UNIV Sequence */

#define	TCAP_TAG_TCAP_ACG	 1	/* TCAP ACG Indicators */
#define	TCAP_TAG_TCAP_STA	 2	/* TCAP Standard Announcement */
#define TCAP_TAG_TCAP_CUA	 3	/* TCAP Customized Announcment */
#define	TCAP_TAG_TCAP_TDIG	 4	/* TCAP Digits */
#define	TCAP_TAG_TCAP_SUEC	 5	/* TCAP Standard User Error Code */
#define	TCAP_TAG_TCAP_PDTA	 6	/* TCAP Problem Data */
#define	TCAP_TAG_TCAP_TCGPA	 7	/* TCAP SCCP Calling Party Address */
#define	TCAP_TAG_TCAP_TRSID	 8	/* TCAP Transaction ID */
#define	TCAP_TAG_TCAP_PCTY	 9	/* TCAP Package Type */
#define	TCAP_TAG_TCAP_SKEY	10	/* TCAP Service Key (Constructor) */
#define	TCAP_TAG_TCAP_BISTAT	11	/* TCAP Busy/Idle Status */
#define	TCAP_TAG_TCAP_CFSTAT	12	/* TCAP Call Forwarding Status */
#define	TCAP_TAG_TCAP_ORIGR	13	/* TCAP Origination Restrictions */
#define	TCAP_TAG_TCAP_TERMR	14	/* TCAP Terminating Restrictions */
#define	TCAP_TAG_TCAP_DNMAP	15	/* TCAP DN to Line Service TYpe Mapping */
#define	TCAP_TAG_TCAP_DURTN	16	/* TCAP Duration */
#define	TCAP_TAG_TCAP_RETD	17	/* TCAP Return Data (Constructor) */
#define	TCAP_TAG_TCAP_BCRQ	18	/* TCAP Bearer Capability Requested */
#define	TCAP_TAG_TCAP_BCSUP	19	/* TCAP Bearer Capability Supported */
#define	TCAP_TAG_TCAP_REFID	20	/* TCAP Reference Id */
#define	TCAP_TAG_TCAP_BGROUP	21	/* TCAP Business Group */
#define	TCAP_TAG_TCAP_SNI	22	/* TCAP Signalling Networks Identifier */
#define	TCAP_TAG_TCAP_MWIT	23	/* TCAP Message Waiting Indicator Type */

#define	TCAP_TAG_PRIV_UNI	 1	/* ANSI Unidirectional */
#define	TCAP_TAG_PRIV_QWP	 2	/* ANSI Query w/ permission */
#define	TCAP_TAG_PRIV_QWOP	 3	/* ANSI Query w/o permission */
#define	TCAP_TAG_PRIV_RESP	 4	/* ANSI Response */
#define	TCAP_TAG_PRIV_CWP	 5	/* ANSI Conversaion w/ permission */
#define	TCAP_TAG_PRIV_CWOP	 6	/* ANSI Conversaion w/o permission */
#define	TCAP_TAG_PRIV_TRSID	 7	/* ANSI Transaction Id */
#define	TCAP_TAG_PRIV_CSEQ	 8	/* ANSI Component Sequence */
#define	TCAP_TAG_PRIV_INKL	 9	/* ANSI Invoke (Last) */
#define	TCAP_TAG_PRIV_RRL	10	/* ANSI Return Result (Last) */
#define	TCAP_TAG_PRIV_RER	11	/* ANSI Return Error */
#define	TCAP_TAG_PRIV_REJ	12	/* ANSI Reject */
#define	TCAP_TAG_PRIV_INK	13	/* ANSI Invoke (Not Last) */
#define	TCAP_TAG_PRIV_RR	14	/* ANSI Result (Not Last) */
#define	TCAP_TAG_PRIV_CORID	15	/* ANSI Correlation Id(s) */
#define	TCAP_TAG_PRIV_NOPCO	16	/* ANSI National Operation Code */
#define	TCAP_TAG_PRIV_POPCO	17	/* ANSI Private Operation Code */
#define	TCAP_TAG_PRIV_PSET	18	/* ANSI Parameter Set */
#define TCAP_TAG_PRIV_NECODE	19	/* ANSI National Error Code */
#define TCAP_TAG_PRIV_PECODE	20	/* ANSI Private Error Code */
#define	TCAP_TAG_PRIV_PBCODE	21	/* ANSI Reject Problem Code */
#define	TCAP_TAG_PRIV_PSEQ	21	/* ANSI Parameter Sequence */
#define	TCAP_TAG_PRIV_ABORT	22	/* ANSI Abort */
#define	TCAP_TAG_PRIV_PCAUSE	23	/* ANSI P-Abort Cause */
#define	TCAP_TAG_PRIV_U_ABORT	24	/* ANSI User Abort Information */
#define	TCAP_TAG_PRIV_DIALOG	25	/* ANSI Dialog Portion */
#define	TCAP_TAG_PRIV_VERSION	26	/* ANSI Protocol Version */
#define	TCAP_TAG_PRIV_CONTEXT	27	/* ANSI Integer Application Context */
#define	TCAP_TAG_PRIV_CTX_OID	28	/* ANSI OID Application Context */
#define	TCAP_TAG_PRIV_UINFO	29	/* ANSI User Information */

#define TCAP_TAG_APPL_UNI	1	/* ITUT Unidirectional */
#define TCAP_TAG_APPL_BEGIN	2	/* ITUT Begin Transaction */
#define TCAP_TAG_APPL_END	4	/* ITUT End Transaction */
#define TCAP_TAG_APPL_CONT	5	/* ITUT Continue Transaction */
#define TCAP_TAG_APPL_ABORT	7	/* ITUT Abort Transaction */
#define TCAP_TAG_APPL_ORIGID	8	/* ITUT Origination Transaction Id */
#define TCAP_TAG_APPL_DESTID	9	/* ITUT Destination Transaction Id */
#define TCAP_TAG_APPL_PCAUSE	10	/* ITUT P-Abort Cause */
#define TCAP_TAG_APPL_DIALOG	11	/* ITUT Dialog Portion */
#define TCAP_TAG_APPL_CSEQ	12	/* ITUT Component Portion */

#define TCAP_TAG_APPL_AUDT_PDU	0	/* ITUT AUDT APDU */
#define TCAP_TAG_APPL_AARQ_PDU	0	/* ITUT AARQ APDU */
#define TCAP_TAG_APPL_AARE_PDU	1	/* ITUT AARE APDU */
#define TCAP_TAG_APPL_RLRQ_PDU	2	/* ITUT RLRQ APDU */
#define TCAP_TAG_APPL_RLRE_PDU	3	/* ITUT RLRE APDU */
#define TCAP_TAG_APPL_ABRT_PDU	4	/* ITUT ABRT APDU */

#define TCAP_TAG_CNTX_LID	0	/* Linked Id */
#define TCAP_TAG_CNTX_INK	1	/* Invoke */
#define TCAP_TAG_CNTX_RRL	2	/* Return Result (Last) */
#define TCAP_TAG_CNTX_RER	3	/* Return Error */
#define TCAP_TAG_CNTX_REJ	4	/* Reject */
#define TCAP_TAG_CNTX_RR	7	/* Return Result (Not Last) */

typedef struct sc_msg {
	struct sccp_addr orig;			/* Originating address */
	uchar oaddr[SCCP_MAX_ADDR_LENGTH];	/* Originating address signals */
	struct sccp_addr dest;			/* Destination address */
	uchar daddr[SCCP_MAX_ADDR_LENGTH];	/* Destination address signals */
	ulong pcl;				/* protocol class */
	ulong imp;				/* importance */
	ulong seq;				/* sequence control */
	ulong ret;				/* return option */
	ulong sls;				/* signalling link selection */
	ulong mp;				/* message priority */
} sc_msg_t;

typedef struct tr_msg {
	struct sc_msg sccp;			/* SCCP message */
	ulong type;				/* Message type */
	ulong parms;				/* Bit mask of parms included */
	ulong origid;				/* Originating Transaction Id */
	ulong destid;				/* Destination Transaction Id */
	ulong cause;				/* Abort Cause */
	ulong version;				/* TCAP version flags */
	uchar *dlg_beg;				/* beg of dialog portion */
	uchar *dlg_end;				/* end of dialog portion */
	uchar *cmp_beg;				/* beg of components or u-abort info */
	uchar *cmp_end;				/* end of components or u-abort info */
	uchar *abt_beg;				/* beg of u-abort info */
	uchar *abt_end;				/* end of u-abort info */
} tr_msg_t;

#define TCAP_PTF_ORIGID	(1<<0)	/* Originating Transaction id */
#define TCAP_PTF_DESTID	(1<<1)	/* Destination Transaction id */
#define TCAP_PTF_DLGP	(1<<2)	/* Dialog Portion */
#define TCAP_PTF_CSEQ	(1<<3)	/* Component Portion */

typedef struct tc_msg {
	ulong type;				/* Component type */
	ulong parms;				/* Bit mask of parms included */
	ulong iid;				/* Invoke Id */
	ulong lid;				/* Linked Id */
	ulong opcode;				/* Operation Code */
	ulong ecode;				/* Error Code */
	ulong pcode;				/* Problem Code */
	uchar *prm_beg;				/* beg of parameters */
	uchar *prm_end;				/* end of parameters */
} tc_msg_t;

#define TCAP_PTF_IID	(1<<0)	/* Invoke Id */
#define TCAP_PTF_LID	(1<<1)	/* Linked Id */
#define TCAP_PTF_NOPCO	(1<<2)	/* National Opcode */
#define TCAP_PTF_POPCO	(1<<3)	/* Private Opcode */
#define TCAP_PTF_LOPCO	(1<<4)	/* Local Opcode */
#define TCAP_PTF_PSEQ	(1<<5)	/* Parameter Sequence */
#define TCAP_PTF_NECODE	(1<<6)	/* National Error Code */
#define TCAP_PTF_PECODE	(1<<7)	/* Private Error Code */
#define TCAP_PTF_LECODE	(1<<8)	/* Local Error Code */
#define TCAP_PTF_PCODE	(1<<9)	/* Problem Code */

/*
 *  =========================================================================
 *
 *  TCAP DECODE Message Functions
 *
 *  =========================================================================
 */
/*
 *  General purpose decoding functions.
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int unpack_taglen(uchar **p, uchar **e, ulong * tag, ulong * cls)
{
	ulong len;
	if (*p >= *e)
		return (-EMSGSIZE);
	*cls = *(*p)++;
	if ((*cls & 0x1f) != 0x1f) {	/* tag is not extended */
		*tag = (*cls & 0x1f);
	} else {		/* tag is extended */
		uint8_t ptag;
		*tag = 0;
		if (*p >= *e)
			return (-EMSGSIZE);
		ptag = *(*p)++;
		*tag |= (ptag & 0x7f);
		if (ptag & 0x80) {
			if (*p >= *e)
				return (-EMSGSIZE);
			*tag <<= 7;
			ptag = *(*p)++;
			*tag |= (ptag & 0x7f);
			if (ptag & 0x80) {
				if (*p >= *e)
					return (-EMSGSIZE);
				*tag <<= 7;
				ptag = *(*p)++;
				*tag |= (ptag & 0x7f);
				if (ptag & 0x80) {
					if (*p >= *e)
						return (-EMSGSIZE);
					*tag <<= 7;
					ptag = *(*p)++;
					*tag |= (ptag & 0x7f);
					if (ptag & 0x80)
						return (-EMSGSIZE);
				}
			}
		}
	}
	if (*p >= *e)
		return (-EMSGSIZE);
	len = *(*p)++;
	if ((len & 0x80) != 0x00) {	/* extended length */
		ulong plen = len & 0x7f;
		if (plen > 4 || plen == 0)
			return (-EMSGSIZE);
		len = 0;
		while (plen--) {
			if (*p >= *e)
				return (-EMSGSIZE);
			len |= *(*p)++;
			len <<= 8;
		}
		if (*p + len > *e)
			return (-EMSGSIZE);
		*e = *p + len;
	}
	*cls &= ~0x1f;
	return (0);
}

/* unpack universal primitive class 0 form 0 */
STATIC INLINE int unpack_univ_prim(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0x00)
		return (0);
	return (-EINVAL);
}

/* unpack universal constructor class 0 form 1 */
STATIC INLINE int unpack_univ_cons(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0x20)
		return (0);
	return (-EINVAL);
}

/* unpack application-wide primitive class 1 form 0 */
STATIC INLINE int unpack_appl_prim(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0x80)
		return (0);
	return (-EINVAL);
}

/* unpack application-wide constructor class 1 form 1 */
STATIC INLINE int unpack_appl_cons(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0xa0)
		return (0);
	return (-EINVAL);
}

/* unpack context-specific primitive class 2 form 0 */
STATIC INLINE int unpack_ctxt_prim(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0x40)
		return (0);
	return (-EINVAL);
}

/* unpack context-specific constructor class 2 form 1 */
STATIC INLINE int unpack_ctxt_cons(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0x60)
		return (0);
	return (-EINVAL);
}

/* unpack private primitive class 3 form 0 */
STATIC INLINE int unpack_priv_prim(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0xc0)
		return (0);
	return (-EINVAL);
}

/* unpack private constructor class 3 form 1 */
STATIC INLINE int unpack_priv_cons(uchar **p, uchar **e, ulong * tag)
{
	int err;
	ulong cls;
	if ((err = unpack_taglen(p, e, tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0xe0)
		return (0);
	return (-EINVAL);
}
STATIC INLINE int unpack_implicit_int(uchar **p, uchar *e, ulong * val)
{
	/* FIXME */
	return (-EFAULT);
}
STATIC INLINE int unpack_explicit_int(uchar **p, uchar *e, ulong * val)
{
	/* FIXME */
	return (-EFAULT);
}

/*
 *  =========================================================================
 *
 *  TCAP DECODE Messages (Component (TC) Sub-Layer)
 *
 *  =========================================================================
 *
 *  ANSI PRIVATE-TCAP version of Components.
 *
 *  -------------------------------------------------------------------------
 *
 *  UNPACK ANSI Correlation Ids.
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_priv_corids(uchar **p, uchar *e, struct tc_msg *m, const ulong ctype)
{
	int err;
	ulong tag;
	if ((err = unpack_priv_prim(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_PRIV_CORID)
		return (-EINVAL);
	switch (e - *p) {
	case 0:		/* no ids */
		switch (ctype) {
		case TCAP_CT_INVOKE_L:
		case TCAP_CT_INVOKE_NL:
		case TCAP_CT_REJECT:
			return (0);
		}
		return (-EPROTO);
	case 1:		/* invoke id */
		switch (ctype) {
		default:
		case TCAP_CT_INVOKE_L:
		case TCAP_CT_INVOKE_NL:
		case TCAP_CT_RESULT_L:
		case TCAP_CT_RESULT_NL:
		case TCAP_CT_REJECT:
		case TCAP_CT_ERROR:
			if (*p >= e)
				return (-EMSGSIZE);
			m->iid = *(*p)++;
			m->parms |= TCAP_PTF_IID;
			break;
		}
		return (-EPROTO);
	case 2:		/* invoke id and correlation id */
		switch (ctype) {
		case TCAP_CT_INVOKE_L:
		case TCAP_CT_INVOKE_NL:
			if (*p >= e)
				return (-EMSGSIZE);
			m->iid = *(*p)++;
			m->parms |= TCAP_PTF_IID;
			if (*p >= e)
				return (-EMSGSIZE);
			m->lid = *(*p)++;
			m->parms |= TCAP_PTF_LID;
			return (0);
		}
		return (-EPROTO);
	}
	return (-EINVAL);
}

/*
 *  UNPACK ANSI Opcodes
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_priv_opcode(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if ((err = unpack_priv_prim(p, &e, &tag)))
		return (err);
	if (e - *p != 2)
		return (-EMSGSIZE);
	switch (tag) {
	case TCAP_TAG_PRIV_NOPCO:
		if ((err = unpack_implicit_int(p, e, &m->opcode)))
			return (err);
		m->parms |= TCAP_PTF_NOPCO;
		return (0);
	case TCAP_TAG_PRIV_POPCO:
		if ((err = unpack_implicit_int(p, e, &m->opcode)))
			return (err);
		m->parms |= TCAP_PTF_POPCO;
		return (0);
	}
	return (-EPROTO);
}

/*
 *  UNPACK ANSI Error Codes
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_priv_ecode(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if ((err = unpack_priv_prim(p, &e, &tag)))
		return (err);
	switch (tag) {
	case TCAP_TAG_PRIV_NECODE:
		if ((err = unpack_explicit_int(p, e, &m->ecode)))
			return (err);
		m->parms |= TCAP_PTF_NECODE;
		return (0);
	case TCAP_TAG_PRIV_PECODE:
		if ((err = unpack_explicit_int(p, e, &m->ecode)))
			return (err);
		m->parms |= TCAP_PTF_PECODE;
		return (0);
	}
	return (-EPROTO);
}

/*
 *  UNPACK ANSI Problem Codes
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_priv_pcode(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if ((err = unpack_priv_prim(p, &e, &tag)))
		return (err);
	if ((err = unpack_implicit_int(p, e, &m->pcode)))
		return (err);
	m->parms |= TCAP_PTF_PCODE;
	return (0);
}

/*
 *  UNPACK ANSI Component Parameter Sets
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_priv_pset(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if ((err = unpack_priv_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_PRIV_PSET && tag != TCAP_TAG_PRIV_PSEQ)
		return (-EINVAL);
	m->prm_beg = *p;
	m->prm_end = e;
	*p = e;
	m->parms |= TCAP_PTF_PSEQ;
	return (0);
}

/*
 *  ANSI INVOKE (LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_inkl(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_INVOKE_L;
	if ((err = unpack_priv_corids(p, e, m, TCAP_CT_INVOKE_L)))
		return (err);
	if ((err = unpack_priv_opcode(p, e, m)))
		return (err);
	if ((err = unpack_priv_pset(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ANSI INVOKE (NOT LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_ink(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_INVOKE_NL;
	if ((err = unpack_priv_corids(p, e, m, TCAP_CT_INVOKE_NL)))
		return (err);
	if ((err = unpack_priv_opcode(p, e, m)))
		return (err);
	if ((err = unpack_priv_pset(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ANSI RETURN RESULT (LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_rrl(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_RESULT_L;
	if ((err = unpack_priv_corids(p, e, m, TCAP_CT_RESULT_L)))
		return (err);
	if ((err = unpack_priv_pset(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ANSI RETURN RESULT (NOT LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_rr(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_RESULT_NL;
	if ((err = unpack_priv_corids(p, e, m, TCAP_CT_RESULT_NL)))
		return (err);
	if ((err = unpack_priv_pset(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ANSI RETURN ERROR
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_rer(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_ERROR;
	if ((err = unpack_priv_corids(p, e, m, TCAP_CT_ERROR)))
		return (err);
	if ((err = unpack_priv_ecode(p, e, m)))
		return (err);
	if ((err = unpack_priv_pset(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ANSI REJECT
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_rej(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_REJECT;
	if ((err = unpack_priv_corids(p, e, m, TCAP_CT_REJECT)))
		return (err);
	if ((err = unpack_priv_pcode(p, e, m)))
		return (err);
	if ((err = unpack_priv_pset(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ANSI Component Decoder
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_priv_dec_comp(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if (!(err = unpack_appl_cons(p, &e, &tag)))
		switch (tag) {
		case TCAP_TAG_PRIV_INKL:	/* Invoke (Last) */
			if (!(err = tcap_priv_dec_inkl(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_PRIV_INK:	/* Invoke (Not Last) */
			if (!(err = tcap_priv_dec_ink(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_PRIV_RRL:	/* Return Result (Last) */
			if (!(err = tcap_priv_dec_rrl(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_PRIV_RR:	/* Return Result (Not Last) */
			if (!(err = tcap_priv_dec_rr(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_PRIV_RER:	/* Return Error */
			if (!(err = tcap_priv_dec_rer(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_PRIV_REJ:	/* Reject */
			if (!(err = tcap_priv_dec_rej(p, e, m)))
				return (0);
			break;
		default:
			err = -EINVAL;
			break;
		}
	return (err);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  ITUT APPLICATION-TCAP version of Components.
 *
 *  -------------------------------------------------------------------------
 *
 *  UNPACK ITUT Correlation Ids.
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_univ_iid(uchar **p, uchar *e, struct tc_msg *m)
{
	(void) p;
	(void) e;
	(void) m;
	/* FIXME */
	return (-EFAULT);
}
STATIC INLINE int unpack_univ_lid(uchar **p, uchar *e, struct tc_msg *m)
{
	(void) p;
	(void) e;
	(void) m;
	/* FIXME */
	return (-EFAULT);
}
STATIC INLINE int unpack_univ_corids(uchar **p, uchar *e, struct tc_msg *m, const ulong ctype)
{
	int err;
	switch (ctype) {
	case TCAP_CT_INVOKE_L:
		if ((err = unpack_univ_iid(p, e, m)))
			return (err);
		m->parms |= TCAP_PTF_IID;
		if ((err = unpack_univ_lid(p, e, m)))	/* optional */
			return (err);
		m->parms |= TCAP_PTF_LID;
		return (0);
	case TCAP_CT_RESULT_L:
	case TCAP_CT_RESULT_NL:
	case TCAP_CT_ERROR:
		if ((err = unpack_univ_iid(p, e, m)))
			return (err);
		m->parms |= TCAP_PTF_IID;
		return (0);
	case TCAP_CT_REJECT:
		/* could be NULL */
		if ((err = unpack_univ_iid(p, e, m)))
			return (err);
		m->parms |= TCAP_PTF_IID;
		return (0);
	}
	return (-EFAULT);
}

/*
 *  UNPACK ITUT Opcodes
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_univ_opcode(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if ((err = unpack_univ_prim(p, &e, &tag)))
		return (err);
	if (tag == TCAP_TAG_UNIV_OID)	/* 6 */
		return (-EOPNOTSUPP);	/* we don't support OID opcodes */
	if (tag != TCAP_TAG_UNIV_INT)	/* 2 */
		return (-EPROTO);
	if ((err = unpack_implicit_int(p, e, &m->opcode)))
		return (err);
	m->parms |= TCAP_PTF_LOPCO;
	return (0);
}

/*
 *  UNPACK ITUT Parameter Sequences
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_univ_pseq(uchar **p, uchar *e, struct tc_msg *m, const ulong opt)
{
	int err;
	ulong tag;
	if (opt) {
		if (*p >= e)
			return (0);
		if (**p != (0x20 | TCAP_TAG_UNIV_SEQ))
			return (0);
	}
	if ((err = unpack_univ_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_UNIV_SEQ)
		return (-EINVAL);
	m->prm_beg = *p;
	m->prm_end = e;
	*p = e;
	m->parms |= TCAP_PTF_PSEQ;
	return (0);
}

/*
 *  UNPACK ITUT Result Opcode and Parameter Sequences
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_univ_rseq(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if (*p >= e)
		return (0);
	if (**p != (0x20 | TCAP_TAG_UNIV_SEQ))
		return (0);
	if ((err = unpack_univ_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_UNIV_SEQ)
		return (-EINVAL);
	if ((err = unpack_univ_opcode(p, e, m)))
		return (err);
	if ((err = unpack_univ_pseq(p, e, m, 0)))
		return (err);
	return (0);
}

/*
 *  UNPACK ITUT Error Codes
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_univ_ecode(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if ((err = unpack_univ_prim(p, &e, &tag)))
		return (err);
	switch (tag) {
	case TCAP_TAG_UNIV_INT:
		if ((err = unpack_implicit_int(p, e, &m->ecode)))
			return (err);
		m->parms |= TCAP_PTF_LECODE;
		return (0);
	case TCAP_TAG_UNIV_OID:
		return (-EOPNOTSUPP);	/* don't support global error codes */
	}
	return (-EPROTO);
}

/*
 *  UNPACK ITUT Problem Codes
 *  ------------------------------------------------------------------------
 */
STATIC INLINE int unpack_ctxt_pcode(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	ulong ptype;
	if ((err = unpack_ctxt_prim(p, &e, &tag)))
		return (err);
	ptype = tag;
	if ((err = unpack_implicit_int(p, e, &m->pcode)))
		return (err);
	m->pcode |= ptype << 16;
	m->parms |= TCAP_PTF_PCODE;
	return (0);
}

/*
 *  ITUT INVOKE (LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_ctxt_dec_inkl(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_INVOKE_L;
	if ((err = unpack_univ_corids(p, e, m, TCAP_CT_INVOKE_L)))
		return (err);
	if ((err = unpack_univ_opcode(p, e, m)))
		return (err);
	if ((err = unpack_univ_pseq(p, e, m, 1)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ITUT RETURN RESULT (LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_ctxt_dec_rrl(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_RESULT_L;
	if ((err = unpack_univ_corids(p, e, m, TCAP_CT_RESULT_L)))
		return (err);
	if ((err = unpack_univ_rseq(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ITUT RETURN RESULT (NOT LAST)
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_ctxt_dec_rr(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_RESULT_NL;
	if ((err = unpack_univ_corids(p, e, m, TCAP_CT_RESULT_NL)))
		return (err);
	if ((err = unpack_univ_rseq(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ITUT RETURN ERROR
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_ctxt_dec_rer(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_ERROR;
	if ((err = unpack_univ_corids(p, e, m, TCAP_CT_ERROR)))
		return (err);
	if ((err = unpack_univ_ecode(p, e, m)))
		return (err);
	if ((err = unpack_univ_pseq(p, e, m, 1)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ITUT REJECT
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_ctxt_dec_rej(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	m->type = TCAP_CT_REJECT;
	if ((err = unpack_univ_corids(p, e, m, TCAP_CT_REJECT)))
		return (err);
	if ((err = unpack_ctxt_pcode(p, e, m)))
		return (err);
	if (*p != e)
		return (-EMSGSIZE);
	return (0);
}

/*
 *  ITUT Component Decoder
 *  ------------------------------------------------------------------------
 */
STATIC int tcap_appl_dec_comp(uchar **p, uchar *e, struct tc_msg *m)
{
	int err;
	ulong tag;
	if (!(err = unpack_ctxt_cons(p, &e, &tag)))
		switch (tag) {
		case TCAP_TAG_CNTX_INK:	/* Invoke */
			if (!(err = tcap_ctxt_dec_inkl(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_CNTX_RRL:	/* Return Result (Last) */
			if (!(err = tcap_ctxt_dec_rrl(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_CNTX_RER:	/* Return Error */
			if (!(err = tcap_ctxt_dec_rer(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_CNTX_REJ:	/* Reject */
			if (!(err = tcap_ctxt_dec_rej(p, e, m)))
				return (0);
			break;
		case TCAP_TAG_CNTX_RR:	/* Return Result (Not Last) */
			if (!(err = tcap_ctxt_dec_rr(p, e, m)))
				return (0);
			break;
		default:
			err = -EINVAL;
			break;
		}
	return (err);
}

/*
 *  =========================================================================
 *
 *  TCAP DECODE Messages (Transaction (TR) Sub-Layer)
 *
 *  =========================================================================
 */
/*
 *  PRIVATE-TCAP version of Packages.  (Used by the TR sub-layer.)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int unpack_priv_trsid(uchar **p, uchar *e, struct tr_msg *m, const ulong mtype)
{
	int err;
	ulong tag;
	if ((err = unpack_priv_prim(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_PRIV_TRSID)
		return (-EINVAL);
	switch (e - *p) {
	case 0:		/* no transaction ids */
		switch (mtype) {
		case TCAP_MT_UNI:
			return (0);
		}
		return (-EPROTO);
	case 4:		/* orig or dest transaction id */
		switch (mtype) {
		case TCAP_MT_QWP:
		case TCAP_MT_QWOP:
		case TCAP_MT_RESP:
		case TCAP_MT_ABORT:
			unpack_implicit_int(p, e, &m->origid);
			m->parms |= TCAP_PTF_ORIGID;
			return (0);
		}
		return (-EPROTO);
	case 8:		/* orig and dest transaction id */
		switch (mtype) {
		case TCAP_MT_CWP:
		case TCAP_MT_CWOP:
			unpack_implicit_int(p, e - 4, &m->origid);
			m->parms |= TCAP_PTF_ORIGID;
			unpack_implicit_int(p, e, &m->destid);
			m->parms |= TCAP_PTF_DESTID;
			return (0);
		}
		return (-EPROTO);
	}
	return (-EMSGSIZE);
}

/* ANSI dialog portion */
STATIC int unpack_priv_dialog_portion(uchar **p, uchar *e, struct tr_msg *m)
{
	ulong err;
	ulong tag;
	if (*p >= e)
		return (0);
	if (**p != (0xc0 | TCAP_TAG_PRIV_DIALOG))
		return (0);
	if ((err = unpack_priv_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_PRIV_DIALOG)
		return (-EINVAL);
	m->dlg_beg = *p;
	m->dlg_end = e;
	*p = e;
	m->parms |= TCAP_PTF_DLGP;
	return (0);
}

/* ANSI component portion */
STATIC int unpack_priv_component_portion(uchar **p, uchar *e, struct tr_msg *m, ulong opt)
{
	ulong err;
	ulong tag;
	if (opt) {
		if (*p >= e)
			return (0);
		if (**p != (0xc0 | TCAP_TAG_PRIV_CSEQ))
			return (0);
	}
	if ((err = unpack_priv_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_PRIV_CSEQ)
		return (-EINVAL);
	m->cmp_beg = *p;
	m->cmp_end = e;
	*p = e;
	m->parms |= TCAP_PTF_CSEQ;
	return (0);
}

/* ANSI abort cause information */
STATIC int unpack_priv_cause_info(uchar **p, uchar *e, struct tr_msg *m)
{
	(void) p;
	(void) e;
	(void) m;
	/* FIXME */
	return (-EFAULT);
}
STATIC int tcap_priv_dec_uni(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_UNI;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_UNI)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_component_portion(&p, e, m, 0)))
		return (err);
	return (0);
}
STATIC int tcap_priv_dec_qwp(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_QWP;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_QWP)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_priv_dec_qwop(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_QWOP;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_QWOP)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_priv_dec_cwp(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_CWP;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_CWP)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_priv_dec_cwop(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_CWOP;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_CWOP)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_priv_dec_resp(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_RESP;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_RESP)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_priv_dec_abt(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_ABORT;
	if ((err = unpack_priv_trsid(&p, e, m, TCAP_MT_ABORT)))
		return (err);
	if ((err = unpack_priv_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_priv_cause_info(&p, e, m)))
		return (err);
	return (0);
}

/*
 *  APPLICATION-TCAP version of Packages.  (Used by the TR sub-layer.)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int unpack_appl_origid(uchar **p, uchar *e, struct tr_msg *m)
{
	ulong err;
	ulong tag;
	if ((err = unpack_appl_prim(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_APPL_ORIGID)
		return (-EPROTO);
	if (4 < e - *p || e - *p < 1)
		return (-EPROTO);
	unpack_implicit_int(p, e, &m->origid);
	m->parms |= TCAP_PTF_ORIGID;
	return (0);
}
STATIC INLINE int unpack_appl_destid(uchar **p, uchar *e, struct tr_msg *m)
{
	ulong err;
	ulong tag;
	if ((err = unpack_appl_prim(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_APPL_DESTID)
		return (-EPROTO);
	if (4 < e - *p || e - *p < 1)
		return (-EPROTO);
	unpack_implicit_int(p, e, &m->destid);
	m->parms |= TCAP_PTF_DESTID;
	return (0);
}
STATIC INLINE int unpack_appl_trsid(uchar **p, uchar *e, struct tr_msg *m, const ulong mtype)
{
	ulong err;
	switch (mtype) {
	case TCAP_MT_UNI:
		return (0);
	case TCAP_MT_QWP:
		if ((err = unpack_appl_origid(p, e, m)))
			return (err);
		return (0);
	case TCAP_MT_CWP:
		if ((err = unpack_appl_origid(p, e, m)))
			return (err);
		if ((err = unpack_appl_destid(p, e, m)))
			return (err);
		return (0);
	case TCAP_MT_RESP:
	case TCAP_MT_ABORT:
		if ((err = unpack_appl_destid(p, e, m)))
			return (err);
		return (0);
	}
	return (-EFAULT);
}

/* ITU-T dialog portion */
STATIC int unpack_appl_dialog_portion(uchar **p, uchar *e, struct tr_msg *m)
{
	ulong err;
	ulong tag;
	if (*p >= e)
		return (0);
	if (**p != (0x60 | TCAP_TAG_APPL_DIALOG))
		return (0);
	if ((err = unpack_appl_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_APPL_DIALOG)
		return (-EINVAL);
	m->dlg_beg = *p;
	m->dlg_end = e;
	*p = e;
	m->parms |= TCAP_PTF_DLGP;
	return (0);
}

/* ITU-T component portion */
STATIC int unpack_appl_component_portion(uchar **p, uchar *e, struct tr_msg *m, ulong opt)
{
	ulong err;
	ulong tag;
	if (opt) {
		if (*p >= e)
			return (0);
		if (**p != (0x60 | TCAP_TAG_APPL_CSEQ))
			return (0);
	}
	if ((err = unpack_appl_cons(p, &e, &tag)))
		return (err);
	if (tag != TCAP_TAG_APPL_CSEQ)
		return (-EINVAL);
	m->cmp_beg = *p;
	m->cmp_end = e;
	*p = e;
	m->parms |= TCAP_PTF_CSEQ;
	return (0);
}

/* ITU-T abort cause information */
STATIC int unpack_appl_cause_info(uchar **p, uchar *e, struct tr_msg *m)
{
	(void) p;
	(void) e;
	(void) m;
	/* FIXME */
	return (-EFAULT);
}
STATIC int tcap_appl_dec_uni(uchar *p, uchar *e, struct tr_msg *m)
{
	/* Application Context, Component Sequence */
	int err;
	m->type = TCAP_MT_UNI;
	if ((err = unpack_appl_trsid(&p, e, m, TCAP_MT_UNI)))
		return (err);
	if ((err = unpack_appl_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_appl_component_portion(&p, e, m, 0)))
		return (err);
	return (0);
}
STATIC int tcap_appl_dec_beg(uchar *p, uchar *e, struct tr_msg *m)
{
	/* Originating Transaction Id, Application Context, Component Sequence */
	int err;
	m->type = TCAP_MT_QWP;	/* Query with permission to release */
	if ((err = unpack_appl_trsid(&p, e, m, TCAP_MT_QWP)))
		return (err);
	if ((err = unpack_appl_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_appl_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_appl_dec_cnt(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	m->type = TCAP_MT_CWP;	/* Conversation with permission to release */
	if ((err = unpack_appl_trsid(&p, e, m, TCAP_MT_CWP)))
		return (err);
	if ((err = unpack_appl_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_appl_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_appl_dec_end(uchar *p, uchar *e, struct tr_msg *m)
{
	/* Dest Transaction Id, Application Context, Component Sequence */
	int err;
	m->type = TCAP_MT_RESP;	/* Response */
	if ((err = unpack_appl_trsid(&p, e, m, TCAP_MT_RESP)))
		return (err);
	if ((err = unpack_appl_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_appl_component_portion(&p, e, m, 1)))
		return (err);
	return (0);
}
STATIC int tcap_appl_dec_abt(uchar *p, uchar *e, struct tr_msg *m)
{
	/* Dest Transaction Id, P-Abort-Cause prim or U-Abort-Info const */
	int err;
	if ((err = unpack_appl_trsid(&p, e, m, TCAP_MT_ABORT)))
		return (err);
	if ((err = unpack_appl_dialog_portion(&p, e, m)))
		return (err);
	if ((err = unpack_appl_cause_info(&p, e, m)))
		return (err);
	return (0);
}

/*
 *  TCAP Package decoder.  (Only decodes TR Sub-Layer.)
 *  -------------------------------------------------------------------------
 */
STATIC int tcap_dec_msg(uchar *p, uchar *e, struct tr_msg *m)
{
	int err;
	ulong tag;
	ulong cls;
	if ((err = unpack_taglen(&p, &e, &tag, &cls)))
		return (err);
	if ((cls & 0xe0) == 0xe0) {	/* private constructor */
		switch (tag) {
		case TCAP_TAG_PRIV_UNI:	/* Unidirectional */
			return tcap_priv_dec_uni(p, e, m);
		case TCAP_TAG_PRIV_QWP:	/* Begin or Query w/ permission */
			return tcap_priv_dec_qwp(p, e, m);
		case TCAP_TAG_PRIV_QWOP:	/* Query w/o permission */
			return tcap_priv_dec_qwop(p, e, m);
		case TCAP_TAG_PRIV_RESP:	/* End or Response */
			return tcap_priv_dec_resp(p, e, m);
		case TCAP_TAG_PRIV_CWP:	/* Continue or Conversation w/ permission */
			return tcap_priv_dec_cwp(p, e, m);
		case TCAP_TAG_PRIV_CWOP:	/* Conversation w/o permission */
			return tcap_priv_dec_cwop(p, e, m);
		case TCAP_TAG_PRIV_ABORT:	/* Abort */
			return tcap_priv_dec_abt(p, e, m);
		}
	}
	if ((cls & 0xe0) == 0xa0) {	/* application-wide constructor */
		switch (tag) {
		case TCAP_TAG_APPL_UNI:	/* Unidirectional */
			return tcap_appl_dec_uni(p, e, m);
		case TCAP_TAG_APPL_BEGIN:	/* Begin */
			return tcap_appl_dec_beg(p, e, m);
		case TCAP_TAG_APPL_END:	/* End */
			return tcap_appl_dec_end(p, e, m);
		case TCAP_TAG_APPL_CONT:	/* Continue */
			return tcap_appl_dec_cnt(p, e, m);
		case TCAP_TAG_APPL_ABORT:	/* Abort */
			return tcap_appl_dec_abt(p, e, m);
		}
	}
	return (-EINVAL);
}

/*
 *  =========================================================================
 *
 *  TCAP ENCODE Message Functions
 *
 *  =========================================================================
 */
/*
 *  General purpose encoding functions.
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int len_size(ulong len)
{
	if (!(len & 0xffffff80))
		return (1);
	if (len & 0xff000000)
		return (5);
	if (len & 0xffff0000)
		return (4);
	if (len & 0xffffff00)
		return (3);
	if (len & 0xffffff80)
		return (2);
	return (1);
}
STATIC INLINE int tag_size(ulong tag)
{
	if (!(tag & 0xffffffc0))
		return (1);
	if (tag & 0xf0000000)
		return (6);
	if (tag & 0xffe00000)
		return (5);
	if (tag & 0xffffc000)
		return (4);
	if (tag & 0xffffff80)
		return (3);
	if (tag & 0xffffffc0)
		return (2);
	return (1);
}
STATIC INLINE void pack_tag_class(uchar **p, ulong tag, ulong fcls)
{
	int n;
	if (tag < 32) {
		*(*p)++ = tag | fcls;
		return;
	}
	n = tag_size(tag) - 1;
	*(*p)++ = 0x1f | fcls;
	switch (n) {
	case 5:
		*(*p)++ = (tag >> 28) | 0x80;
	case 4:
		*(*p)++ = (tag >> 21) | 0x80;
	case 3:
		*(*p)++ = (tag >> 14) | 0x80;
	case 2:
		*(*p)++ = (tag >> 7) | 0x80;
	case 1:
		*(*p)++ = (tag >> 0) | 0x00;
	}
	return;
}
STATIC INLINE void pack_tag_univ_prim(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0x00);
}
STATIC INLINE void pack_tag_univ_cons(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0x20);
}
STATIC INLINE void pack_tag_appl_prim(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0x40);
}
STATIC INLINE void pack_tag_appl_cons(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0x60);
}
STATIC INLINE void pack_tag_cntx_prim(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0x80);
}
STATIC INLINE void pack_tag_cntx_cons(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0xa0);
}
STATIC INLINE void pack_tag_priv_prim(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0xc0);
}
STATIC INLINE void pack_tag_priv_cons(uchar **p, ulong tag)
{
	return pack_tag_class(p, tag, 0xe0);
}
STATIC INLINE void pack_len(uchar **p, ulong len)
{
	ulong n;
	if (len < 128) {
		*(*p)++ = len;
		return;
	}
	n = len_size(len) - 1;
	*(*p)++ = 0x80 | n;
	switch (n) {
	case 4:
		*(*p)++ = len >> 24;
	case 3:
		*(*p)++ = len >> 16;
	case 2:
		*(*p)++ = len >> 8;
	case 1:
		*(*p)++ = len >> 0;
	}
	return;
}

/*
 *  =========================================================================
 *
 *  TCAP ENCODE Messages (Transaction (TR) Sub-Layer).
 *
 *  =========================================================================
 *
 *  ANSI TCAP Message (Transaction (TR) Sub-Layer) encoding.
 *
 *  -------------------------------------------------------------------------
 *
 *  ANSI UNI (Unidirectional)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_uni(mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(0);
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_UNI) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_UNI);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, 0);
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ANSI QWP (Query with permission)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_qwp(ulong origid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(sizeof(origid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(origid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_QWP) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_QWP);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, sizeof(origid));
		pack_int(*mp->b_wptr, origid, sizeof(origid));
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ANSI QWOP (Query without permission)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_qwop(ulong origid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(sizeof(origid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(origid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_QWOP) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_QWOP);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, sizeof(origid));
		pack_int(*mp->b_wptr, origid, sizeof(origid));
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ANSI RESP (Response)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_resp(ulong destid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_RESP) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_RESP);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, sizeof(destid));
		pack_int(*mp->b_wptr, destid, sizeof(destid));
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ANSI CWP (Conversation with permission)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_cwp(ulong origid, ulong destid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(sizeof(origid) + sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(origid) + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_CWP) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CWP);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, sizeof(origid) + sizeof(destid));
		pack_int(*mp->b_wptr, origid, sizeof(origid));
		pack_int(*mp->b_wptr, destid, sizeof(destid));
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ANSI CWOP (Conversation without permission)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_cwop(ulong origid, ulong destid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(sizeof(origid) + sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(origid) + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_CWOP) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CWOP);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, sizeof(origid) + sizeof(destid));
		pack_int(*mp->b_wptr, origid, sizeof(origid));
		pack_int(*mp->b_wptr, destid, sizeof(destid));
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ANSI ABORT (Abort)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_abt(ulong destid, mblk_t *dp, mblk_t *ep)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_PRIV_TRSID) + len_size(sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_PRIV_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PRIV_CSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_PRIV_ABORT) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		/* 
		 *  FIXME for cause information rather than components
		 */
		mp->b_datap->db_type = M_DATA;
		pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_ABORT);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_priv_prim(&mp->b_wptr, TCAP_TAG_PRIV_TRSID);
		pack_len(&mp->b_wptr, sizeof(destid));
		pack_int(*mp->b_wptr, destid, sizeof(destid));
		if (clen && !dlen) {	/* common case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case */
			pack_tag_priv_cons(&mp->b_wptr, TCAP_TAG_PRIV_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* even odder case */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_priv_cons(&mc->b_wptr, TCAP_TAG_PRIV_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  ITUT TCAP Message (Transaction (TR) Sub-Layer) encoding.
 *
 *  -------------------------------------------------------------------------
 *
 *  ITUT UNI (Unidirectional)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_appl_enc_uni(mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = 0;
	dlen = dlg_len ? (tag_size(TCAP_TAG_APPL_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PAPPLCSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(origid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_APPL_UNI) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_UNI);
		pack_len(&mp->b_wptr, pkg_len);
		if (clen && !dlen) {	/* common case, components no dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case, dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* oddbal case, dialog and components */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_appl_cons(&mc->b_wptr, TCAP_TAG_APPL_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ITUT BEG (Begin)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_appl_enc_beg(ulong origid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_APPL_ORIGID) + len_size(sizeof(origid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_APPL_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PAPPLCSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(origid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_APPL_BEGIN) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_BEGIN);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_appl_prim(&mp->b_wptr, TCAP_TAG_APPL_ORIGID);
		pack_len(&mp->b_wptr, sizeof(origid));
		pack_int(&mp->b_wptr, origid, sizeof(origid));
		if (clen && !dlen) {	/* common case, components no dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case, dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* oddbal case, dialog and components */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_appl_cons(&mc->b_wptr, TCAP_TAG_APPL_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ITUT END (End)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_appl_enc_end(ulong destid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_APPL_DESTID) + len_size(sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_APPL_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PAPPLCSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_APPL_END) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_END);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_appl_prim(&mp->b_wptr, TCAP_TAG_APPL_DESTID);
		pack_len(&mp->b_wptr, sizeof(destid));
		pack_int(&mp->b_wptr, destid, sizeof(destid));
		if (clen && !dlen) {	/* common case, components no dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case, dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* oddbal case, dialog and components */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_appl_cons(&mc->b_wptr, TCAP_TAG_APPL_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ITUT CONT (Continue)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_appl_enc_cont(ulong origid, ulong destid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen =
	    tag_size(TCAP_TAG_APPL_ORIGID) + len_size(sizeof(origid)) + tag_size(TCAP_TAG_APPL_DESTID) +
	    len_size(sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_APPL_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PAPPLCSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_APPL_CONT) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_CONT);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_appl_prim(&mp->b_wptr, TCAP_TAG_APPL_ORIGID);
		pack_len(&mp->b_wptr, sizeof(origid));
		pack_int(&mp->b_wptr, origid, sizeof(origid));
		pack_tag_appl_prim(&mp->b_wptr, TCAP_TAG_APPL_DESTID);
		pack_len(&mp->b_wptr, sizeof(destid));
		pack_int(&mp->b_wptr, destid, sizeof(destid));
		if (clen && !dlen) {	/* common case, components no dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case, dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* oddbal case, dialog and components */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_appl_cons(&mc->b_wptr, TCAP_TAG_APPL_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  ITUT ABORT (Abort)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_appl_enc_abt(ulong destid, mblk_t *dp, mblk_t *cp)
{
	mblk_t *mp, *mc;
	size_t pkg_len, dlg_len, cmp_len, mlen, hlen, ilen, plen, dlen, clen;
	/* 
	 *  TODO: reduce these to constants where possible
	 */
	dlg_len = dp ? msgdsize(dp) : 0;
	cmp_len = cp ? msgdsize(cp) : 0;
	ilen = tag_size(TCAP_TAG_APPL_DESTID) + len_size(sizeof(destid));
	dlen = dlg_len ? (tag_size(TCAP_TAG_APPL_DLGP) + len_size(dlg_len)) : 0;
	clen = cmp_len ? (tag_size(TCAP_TAG_PAPPLCSEQ) + len_size(cmp_len)) : 0;
	pkg_len = ilen + sizeof(destid) + dlen + dlg_len + clen + cmp_len;
	hlen = tag_size(TCAP_TAG_APPL_ABORT) + len_size(pkg_len)
	    mlen = hlen + ilen + dlen ? dlen : clen;
	if ((mp = allocb(mlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_ABORT);
		pack_len(&mp->b_wptr, pkg_len);
		pack_tag_appl_prim(&mp->b_wptr, TCAP_TAG_APPL_DESTID);
		pack_len(&mp->b_wptr, sizeof(destid));
		pack_int(&mp->b_wptr, destid, sizeof(destid));
		/* 
		 *  FIXME for cause information rather than components
		 */
		if (clen && !dlen) {	/* common case, components no dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_CSEQ);
			pack_len(&mp->b_wptr, cmp_len);
			linkb(mp, cp);
			return (mp);
		}
		if (dlen) {	/* oddball case, dialog */
			pack_tag_appl_cons(&mp->b_wptr, TCAP_TAG_APPL_DLGP);
			pack_len(&mp->b_wptr, dlg_len);
			linkb(mp, dp);
		}
		if (clen) {	/* oddbal case, dialog and components */
			if ((mc = allocb(clen, BPRI_MED))) {
				mc->b_datap->db_type = M_DATA;
				pack_tag_appl_cons(&mc->b_wptr, TCAP_TAG_APPL_CSEQ);
				pack_len(&mc->b_wptr, cmp_len);
				linkb(mp, mc);
				linkb(mp, cp);
				return (mp);
			}
			freeb(mp);
			return (NULL);
		}
	}
	return (mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  ANSI TCAP Component (Component (TC) Sub-Layer) encoding.
 *
 *  -------------------------------------------------------------------------
 *
 *  ANSI INKL (Invoke Last)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_inkl(ulong * iid, ulong * lid, ulong opclass, ulong opcode, mblk_t *pseq)
{
}

/*
 *  ANSI INK  (Invoke Not Last)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_ink(ulong * iid, ulong * lid, ulong opclass, ulong opcode, mblk_t *pseq)
{
}

/*
 *  ANSI RRL  (Return Result Last)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_rrl(ulong * iid, mblk_t *pseq)
{
}

/*
 *  ANSI RR   (Return Result Not Last)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_rr(ulong * iid, mblk_t *pseq)
{
}

/*
 *  ANSI RER  (Return Error)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_rer(ulong * iid, ulong ecode, mblk_t *pseq)
{
}

/*
 *  ANSI REJ  (Reject)
 *  -------------------------------------------------------------------------
 */
STATIC INLINE mblk_t *tcap_priv_enc_rej(ulong * iid, ulong pcode, mblk_t *pseq)
{
}

/*
 *  -------------------------------------------------------------------------
 *
 *  ITUT TCAP Component (Component (TC) Sub-Layer) encoding.
 *
 *  -------------------------------------------------------------------------
 *
 *  ITUT INKL (Invoke Last)
 *  -------------------------------------------------------------------------
 */
/*
 *  ITUT RRL  (Return Result Last)
 *  -------------------------------------------------------------------------
 */
/*
 *  ITUT RR   (Return Result Not Last)
 *  -------------------------------------------------------------------------
 */
/*
 *  ITUT RER  (Return Error)
 *  -------------------------------------------------------------------------
 */
/*
 *  ITUT REJ  (Reject)
 *  -------------------------------------------------------------------------
 */

/*
 *  =========================================================================
 *
 *  OPTION Handling
 *
 *  =========================================================================
 */

typedef struct tcap_var {
	uchar *ptr;
	size_t len;
} tcap_var_t;

typedef struct tcap_opts {
	ulong flags;				/* success flags */
	tcap_var_t ctx;				/* application context */
	tcap_var_t inf;				/* user information */
	t_uscalar_t *pcl;			/* protocol class */
	t_uscalar_t *ret;			/* return option */
	t_uscalar_t *imp;			/* importance */
	t_uscalar_t *seq;			/* sequence control */
	t_uscalar_t *pri;			/* priority */
	t_uscalar_t *sls;			/* signalling link selection */
	t_uscalar_t *mp;			/* message priority */
} tcap_opts_t;

#define TF_OPT_APP	(1<<0)	/* application context */
#define TF_OPT_INF	(1<<1)	/* user information */
#define TF_OPT_PCL	(1<<2)	/* protocol class */
#define TF_OPT_RET	(1<<3)	/* return option */
#define TF_OPT_IMP	(1<<4)	/* importance */
#define TF_OPT_SEQ	(1<<5)	/* sequence control */
#define TF_OPT_PRI	(1<<6)	/* priority */
#define TF_OPT_SLS	(1<<7)	/* signalling link selection */
#define TF_OPT_MP	(1<<8)	/* message priority */

#define _T_ALIGN_SIZEOF(s) \
	((sizeof((s)) + _T_ALIGN_SIZE - 1) & ~(_T_ALIGN_SIZE - 1))
STATIC size_t tcap_opts_size(struct tcap *tcap, struct tcap_opts *ops)
{
	size_t len = 0;
	if (ops) {
		const size_t hlen = sizeof(struct t_opthdr);	/* 32 bytes */
		if (ops->ctx.ptr)
			len += hlen + ops->ctx.len;
		if (ops->inf.ptr)
			len += hlen + ops->inf.len;
		if (ops->pcl)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->pcl));
		if (ops->ret)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->ret));
		if (ops->imp)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->imp));
		if (ops->seq)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->seq));
		if (ops->pri)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->pri));
		if (ops->sls)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->sls));
		if (ops->mp)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->mp));
	}
	return (len);
}
STATIC void tcap_build_opts(struct tcap *tcap, struct tcap_opts *ops, uchar *p)
{
	if (ops) {
		struct t_opthdr *oh;
		const size_t hlen = sizeof(struct t_opthdr);
		if (ops->ctx.ptr) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + ops->ctx.len;
			oh->level = T_SS7_TCAP;
			oh->name = T_TCAP_APP_CTX;
			oh->status = (ops->flags & TF_OPT_APP) ? T_SUCCESS : T_FAILURE;
			bcopy(ops->ctx.ptr, p, ops->ctx.len);
			p += _T_ALIGN_SIZEOF(ops->ctx.len);
		}
		if (ops->inf.ptr) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + ops->inf.len;
			oh->level = T_SS7_TCAP;
			oh->name = T_TCAP_USER_INFO;
			oh->status = (ops->flags & TF_OPT_INF) ? T_SUCCESS : T_FAILURE;
			bcopy(ops->inf.ptr, p, ops->inf.len);
			p += _T_ALIGN_SIZEOF(ops->inf.len);
		}
		if (ops->pcl) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->pcl));
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_PCLASS;
			oh->status = (ops->flags & TF_OPT_PCL) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->pcl)) p) = *(ops->pcl);
			p += _T_ALIGN_SIZEOF(*ops->pcl);
		}
		if (ops->ret) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->ret));
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_RET_ERROR;
			oh->status = (ops->flags & TF_OPT_RET) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->ret)) p) = *(ops->ret);
			p += _T_ALIGN_SIZEOF(*ops->ret);
		}
		if (ops->imp) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->imp));
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_IMPORTANCE;
			oh->status = (ops->flags & TF_OPT_IMP) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->imp)) p) = *(ops->imp);
			p += _T_ALIGN_SIZEOF(*ops->imp);
		}
		if (ops->seq) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->seq));
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_SEQ_CTRL;
			oh->status = (ops->flags & TF_OPT_SEQ) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->seq)) p) = *(ops->seq);
			p += _T_ALIGN_SIZEOF(*ops->seq);
		}
		if (ops->pri) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->pri));
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_PRIORITY;
			oh->status = (ops->flags & TF_OPT_PRI) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->pri)) p) = *(ops->pri);
			p += _T_ALIGN_SIZEOF(*ops->pri);
		}
		if (ops->sls) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->sls));
			oh->level = T_SS7_MTP;
			oh->name = T_MTP_SLS;
			oh->status = (ops->flags & TF_OPT_SLS) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->sls)) p) = *(ops->sls);
			p += _T_ALIGN_SIZEOF(*ops->sls);
		}
		if (ops->mp) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->mp));
			oh->level = T_SS7_MTP;
			oh->name = T_MTP_MP;
			oh->status = (ops->flags & TF_OPT_MP) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->mp)) p) = *(ops->mp);
			p += _T_ALIGN_SIZEOF(*ops->mp);
		}
	}
}
STATIC int tcap_parse_opts(struct tcap *tcap, struct tcap_opts *ops, uchar *op, size_t len)
{
	struct t_opthdr *oh;
	for (oh = _T_OPT_FIRSTHDR_OFS(op, len, 0); oh; oh = _T_OPT_NEXTHDR_OFS(op, len, oh, 0)) {
		switch (oh->level) {
		case T_SS7_TCAP:
			switch (oh->name) {
			case T_TCAP_APP_CTX:
				ops->ctx.ptr = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->ctx.len = oh->len - sizeof(*oh);
				ops->flags |= TF_OPT_APP;
				continue;
			case T_TCAP_USER_INFO:
				ops->inf.ptr = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->inf.len = oh->len - sizeof(*oh);
				ops->flags |= TF_OPT_INF;
				continue;
			}
			break;
		case T_SS7_SCCP:
			switch (oh->name) {
			case T_SCCP_PCLASS:
				ops->pcl = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_PCL;
				continue;
			case T_SCCP_RET_ERROR:
				ops->ret = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_RET;
				continue;
			case T_SCCP_IMPORTANCE:
				ops->imp = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_IMP;
				continue;
			case T_SCCP_SEQ_CTRL:
				ops->seq = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_SEQ;
				continue;
			case T_SCCP_PRIORITY:
				ops->pri = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_PRI;
				continue;
			}
			break;
		case T_SS7_MTP:
			switch (oh->name) {
			case T_MTP_SLS:
				ops->sls = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_SLS;
				continue;
			case T_MTP_MP:
				ops->mp = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= TF_OPT_MP;
				continue;
			}
			break;
			break;
		}
	}
	if (oh)
		return (TBADOPT);
	return (0);
}

/*
 *  =========================================================================
 *
 *  OPTIONS handling
 *
 *  =========================================================================
 */
STATIC int tcap_opt_check(struct tcap *tcap, struct tcap_opts *ops)
{
	if (ops && ops->flags) {
		ops->flags = 0;
		if (ops->ctx.ptr && ops->ctx.len <= TCAP_BUF_MAXLEN)
			ops->flags |= TF_OPT_APP;
		if (ops->inf.ptr && ops->inf.len <= TCAP_BUF_MAXLEN)
			ops->flags |= TF_OPT_INF;
		if (ops->pcl)
			ops->flags |= TF_OPT_PCL;
		if (ops->ret)
			ops->flags |= TF_OPT_RET;
		if (ops->imp)
			ops->flags |= TF_OPT_IMP;
		if (ops->seq)
			ops->flags |= TF_OPT_SEQ;
		if (ops->pri)
			ops->flags |= TF_OPT_PRI;
		if (ops->sls)
			ops->flags |= TF_OPT_SLS;
		if (ops->mp)
			ops->flags |= TF_OPT_MP;
	}
	return (0);
}
STATIC int tcap_opt_default(struct tcap *tcap, struct tcap_opts *ops)
{
	if (ops) {
		int flags = ops->flags;
		ops->flags = 0;
		if (!flags || ops->ctx.ptr) {
			ops->ctx.ptr = opt_defaults.ctx.buf;
			ops->ctx.len = opt_defaults.ctx.len;
			ops->flags |= TF_OPT_APP;
		}
		if (!flags || ops->inf.ptr) {
			ops->inf.ptr = opt_defaults.inf.buf;
			ops->inf.len = opt_defaults.inf.len;
			ops->flags |= TF_OPT_INF;
		}
		if (!flags || ops->pcl) {
			ops->pcl = &opt_defaults.pcl;
			ops->flags |= TF_OPT_PCL;
		}
		if (!flags || ops->ret) {
			ops->ret = &opt_defaults.ret;
			ops->flags |= TF_OPT_RET;
		}
		if (!flags || ops->imp) {
			ops->imp = &opt_defaults.imp;
			ops->flags |= TF_OPT_IMP;
		}
		if (!flags || ops->seq) {
			ops->seq = &opt_defaults.seq;
			ops->flags |= TF_OPT_SEQ;
		}
		if (!flags || ops->pri) {
			ops->pri = &opt_defaults.pri;
			ops->flags |= TF_OPT_PRI;
		}
		if (!flags || ops->sls) {
			ops->sls = &opt_defaults.sls;
			ops->flags |= TF_OPT_SLS;
		}
		if (!flags || ops->mp) {
			ops->mp = &opt_defaults.mp;
			ops->flags |= TF_OPT_MP;
		}
		return (0);
	}
	swerr();
	return (-EFAULT);
}
STATIC int tcap_opt_current(struct tcap *tcap, struct tcap_opts *ops)
{
	int flags = ops->flags;
	ops->flags = 0;
	if (!flags || ops->ctx.ptr) {
		ops->ctx.ptr = tcap->options.ctx.buf;
		ops->ctx.len = tcap->options.ctx.len;
		ops->flags |= TF_OPT_APP;
	}
	if (!flags || ops->inf.ptr) {
		ops->inf.ptr = tcap->options.inf.buf;
		ops->inf.len = tcap->options.inf.len;
		ops->flags |= TF_OPT_INF;
	}
	if (!flags || ops->pcl) {
		ops->pcl = &tcap->options.pcl;
		ops->flags |= TF_OPT_PCL;
	}
	if (!flags || ops->ret) {
		ops->ret = &tcap->options.ret;
		ops->flags |= TF_OPT_RET;
	}
	if (!flags || ops->imp) {
		ops->imp = &tcap->options.imp;
		ops->flags |= TF_OPT_IMP;
	}
	if (!flags || ops->seq) {
		ops->seq = &tcap->options.seq;
		ops->flags |= TF_OPT_SEQ;
	}
	if (!flags || ops->pri) {
		ops->pri = &tcap->options.pri;
		ops->flags |= TF_OPT_PRI;
	}
	if (!flags || ops->sls) {
		ops->sls = &tcap->options.sls;
		ops->flags |= TF_OPT_SLS;
	}
	if (!flags || ops->mp) {
		ops->mp = &tcap->options.mp;
		ops->flags |= TF_OPT_MP;
	}
	return (0);
}
STATIC int tcap_opt_negotiate(struct tcap *tcap, struct tcap_opts *ops)
{
	if (ops->flags) {
		ops->flags = 0;
		if (ops->ctx.ptr && ops->ctx.len <= TCAP_BUF_MAXLEN) {
			bcopy(ops->ctx.ptr, tcap->options.ctx.buf, ops->ctx.len);
			tcap->options.ctx.len = ops->ctx.len;
			ops->ctx.ptr = tcap->options.ctx.buf;
			ops->flags |= TF_OPT_APP;
		}
		if (ops->inf.ptr && ops->inf.len <= TCAP_BUF_MAXLEN) {
			bcopy(ops->inf.ptr, tcap->options.inf.buf, ops->inf.len);
			tcap->options.inf.len = ops->inf.len;
			ops->inf.ptr = tcap->options.inf.buf;
			ops->flags |= TF_OPT_INF;
		}
		if (ops->pcl) {
			tcap->options.pcl = *ops->pcl;
			ops->pcl = &tcap->options.pcl;
			ops->flags |= TF_OPT_PCL;
		}
		if (ops->ret) {
			tcap->options.ret = *ops->ret;
			ops->ret = &tcap->options.ret;
			ops->flags |= TF_OPT_RET;
		}
		if (ops->imp) {
			tcap->options.imp = *ops->imp;
			ops->imp = &tcap->options.imp;
			ops->flags |= TF_OPT_IMP;
		}
		if (ops->seq) {
			tcap->options.seq = *ops->seq;
			ops->seq = &tcap->options.seq;
			ops->flags |= TF_OPT_SEQ;
		}
		if (ops->pri) {
			tcap->options.pri = *ops->pri;
			ops->pri = &tcap->options.pri;
			ops->flags |= TF_OPT_PRI;
		}
		if (ops->sls) {
			tcap->options.sls = *ops->sls;
			ops->sls = &tcap->options.sls;
			ops->flags |= TF_OPT_SLS;
		}
		if (ops->mp) {
			tcap->options.mp = *ops->mp;
			ops->mp = &tcap->options.mp;
			ops->flags |= TF_OPT_MP;
		}
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  STATE Changes
 *
 *  =========================================================================
 */

#ifdef _DEBUG
STATIC INLINE const char *tcap_r_state(ulong state)
{
	switch (state) {
	default:
		return ("(unknown)");
	}
}
STATIC INLINE const char *tcap_c_state(ulong state)
{
	switch (state) {
	default:
		return ("(unknown)");
	}
}
STATIC INLINE const char *tcap_t_state(ulong state)
{
	switch (state) {
	case TS_UNBND:
		return ("TS_UNBND");
	case TS_WACK_BREQ:
		return ("TS_WACK_BREQ");
	case TS_WACK_UREQ:
		return ("TS_WACK_UREQ");
	case TS_IDLE:
		return ("TS_IDLE");
	case TS_WACK_OPTREQ:
		return ("TS_WACK_OPTREQ");
	case TS_WACK_CREQ:
		return ("TS_WACK_CREQ");
	case TS_WCON_CREQ:
		return ("TS_WCON_CREQ");
	case TS_WRES_CIND:
		return ("TS_WRES_CIND");
	case TS_WACK_CRES:
		return ("TS_WACK_CRES");
	case TS_DATA_XFER:
		return ("TS_DATA_XFER");
	case TS_WIND_ORDREL:
		return ("TS_WIND_ORDREL");
	case TS_WREQ_ORDREL:
		return ("TS_WREQ_ORDREL");
	case TS_WACK_DREQ6:
		return ("TS_WACK_DREQ6");
	case TS_WACK_DREQ7:
		return ("TS_WACK_DREQ7");
	case TS_WACK_DREQ9:
		return ("TS_WACK_DREQ9");
	case TS_WACK_DREQ10:
		return ("TS_WACK_DREQ10");
	case TS_WACK_DREQ11:
		return ("TS_WACK_DREQ11");
	default:
		return ("(unknown)");
	}
}
STATIC INLINE const char *tcap_m_state(ulong state)
{
	switch (state) {
	default:
		return ("(unknown)");
	}
}
#endif

STATIC INLINE void tcap_set_state(struct tcap *tcap, ulong newstate)
{
	ulong oldstate = tcap->state;
	(void) oldstate;
	printd(("%s: %p: %s <- %s\n", TCAP_DRV_NAME, tcap, tcap_state(newstate), tcap_state(oldstate)));
	tcap->state = newstate;
}
STATIC INLINE ulong tcap_get_state(struct tcap *tcap)
{
	return tcap->state;
}
STATIC INLINE void tcap_set_r_state(struct tcap *tcap, ulong newstate)
{
	ulong oldstate = tcap->i_state;
	(void) oldstate;
	printd(("%s: %p: %s <- %s\n", TCAP_DRV_NAME, tcap, tcap_r_state(newstate), tcap_r_state(oldstate)));
	tcap->i_state = newstate;
}
STATIC INLINE ulong tcap_get_r_state(struct tcap *tcap)
{
	return tcap->i_state;
}
STATIC INLINE void tcap_set_c_state(struct tcap *tcap, ulong newstate)
{
	ulong oldstate = tcap->i_state;
	(void) oldstate;
	printd(("%s: %p: %s <- %s\n", TCAP_DRV_NAME, tcap, tcap_c_state(newstate), tcap_c_state(oldstate)));
	tcap->i_state = newstate;
}
STATIC INLINE ulong tcap_get_c_state(struct tcap *tcap)
{
	return tcap->i_state;
}
STATIC INLINE void tcap_set_t_state(struct tcap *tcap, ulong newstate)
{
	ulong oldstate = tcap->i_state;
	(void) oldstate;
	printd(("%s: %p: %s <- %s\n", TCAP_DRV_NAME, tcap, tcap_t_state(newstate), tcap_t_state(oldstate)));
	tcap->i_state = newstate;
}
STATIC INLINE ulong tcap_get_m_state(struct tcap *tcap)
{
	return tcap->i_state;
}
STATIC INLINE void tcap_set_m_state(struct tcap *tcap, ulong newstate)
{
	ulong oldstate = tcap->i_state;
	(void) oldstate;
	printd(("%s: %p: %s <- %s\n", TCAP_DRV_NAME, tcap, tcap_m_state(newstate), tcap_m_state(oldstate)));
	tcap->i_state = newstate;
}
STATIC INLINE ulong tcap_get_m_state(struct tcap *tcap)
{
	return tcap->i_state;
}

/*
 *  TLI interface state flags
 */
#define TSF_UNBND	( 1 << TS_UNBND		)
#define TSF_WACK_BREQ	( 1 << TS_WACK_BREQ	)
#define TSF_WACK_UREQ	( 1 << TS_WACK_UREQ	)
#define TSF_IDLE	( 1 << TS_IDLE		)
#define TSF_WACK_OPTREQ	( 1 << TS_WACK_OPTREQ	)
#define TSF_WACK_CREQ	( 1 << TS_WACK_CREQ	)
#define TSF_WCON_CREQ	( 1 << TS_WCON_CREQ	)
#define TSF_WRES_CIND	( 1 << TS_WRES_CIND	)
#define TSF_WACK_CRES	( 1 << TS_WACK_CRES	)
#define TSF_DATA_XFER	( 1 << TS_DATA_XFER	)
#define TSF_WIND_ORDREL	( 1 << TS_WIND_ORDREL	)
#define TSF_WREQ_ORDREL	( 1 << TS_WREQ_ORDREL	)
#define TSF_WACK_DREQ6	( 1 << TS_WACK_DREQ6	)
#define TSF_WACK_DREQ7	( 1 << TS_WACK_DREQ7	)
#define TSF_WACK_DREQ9	( 1 << TS_WACK_DREQ9	)
#define TSF_WACK_DREQ10	( 1 << TS_WACK_DREQ10	)
#define TSF_WACK_DREQ11	( 1 << TS_WACK_DREQ11	)
#define TSF_NOSTATES	( 1 << TS_NOSTATES	)

#define TSF_WACK_DREQ	(TSF_WACK_DREQ6 \
			|TSF_WACK_DREQ7 \
			|TSF_WACK_DREQ9 \
			|TSF_WACK_DREQ10 \
			|TSF_WACK_DREQ11)

/* 
 *  =========================================================================
 *
 *  PRIMITIVES
 *
 *  =========================================================================
 */
/* 
 *  -------------------------------------------------------------------------
 *
 *  Primitives sent upstream
 *
 *  -------------------------------------------------------------------------
 */

/* 
 *  M_FLUSH
 *  -----------------------------------
 */
STATIC int m_flush(queue_t *q, queue_t *pq, int band, int flags, int what)
{
	mblk_t *mp;
	if (!(mp = ss7_allocb(q, 2, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_FLUSH;
	*(mp->b_wptr)++ = flags | (band ? FLUSHBAND : 0);
	*(mp->b_wptr)++ = band;
	printd(("%s: %p: <- M_FLUSH\n", TCAP_DRV_NAME, pq));
	putq(pq, mp);
	return (QR_DONE);
      enobufs:
	rare();
	return (-ENOBUFS);
}

/* 
 *  M_ERROR
 *  -----------------------------------
 */
STATIC int m_error(queue_t *q, struct tcap *tcap, int error)
{
	mblk_t *mp;
	int hangup = 0;
	if (error < 0)
		error = -error;
	switch (error) {
	case EBUSY:
	case ENOBUFS:
	case ENOMEM:
	case EAGAIN:
		return (-error);
	case EPIPE:
	case ENETDOWN:
	case EHOSTUNREACH:
		hangup = 1;
	}
	if (!(mp = ss7_allocb(q, 2, BPRI_MED)))
		goto enobufs;
	if (hangup) {
		mp->b_datap->db_type = M_HANGUP;
		tcap_set_state(tcap, NS_NOSTATES);
		printd(("%s: %p: <- M_HANGUP\n", TCAP_DRV_NAME, tcap));
		ss7_oput(tcap->oq, mp);
		return (-error);
	} else {
		mp->b_datap->db_type = M_ERROR;
		*(mp->b_wptr)++ = error < 0 ? -error : error;
		*(mp->b_rptr)++ = error < 0 ? -error : error;
		tcap_set_state(tcap, NS_NOSTATES);
		printd(("%s: %p: <- M_ERROR\n", TCAP_DRV_NAME, tcap));
		ss7_oput(tcap->oq, mp);
		return (QR_DONE);
	}
      enobufs:
	rare();
	return (-ENOBUFS);
}

/* 
 *  TCI Primitives
 *  -------------------------------------------------------------------------
 */
/*
 *  TC_INFO_ACK
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_info_ack(queue_t *q, struct tcap *tcap)
{
	int err;
	mblk_t *mp;
	struct TC_info_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_INFO_ACK;
	p->TSDU_size = FIXME;
	p->ETSDU_size = FIXME;
	p->CDATA_size = FIXME;
	p->DDATA_size = FIXME;
	p->ADDR_size = sizeof(struct sccp_addr) + SCCP_MAX_ADDR_LEN;
	p->OPT_size = FIXME;
	p->TIDU_size = FIXME;
	p->SERV_type = FIXME;
	p->CURRENT_state = tcap_get_c_state(tcap);
	p->PROVIDER_flags = FIXME;
	p->TRPI_version = FIXME;
	printd(("%s: %p: <- TC_INFO_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_BIND_ACK
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_bind_ack(queue_t *q, struct tcap *tcap, struct sccp_addr *add, ulong xact, ulong tok)
{
	int err;
	mblk_t *mp;
	struct TC_bind_ack *p;
	size_t add_len = add ? sizeof(*add) + add->alen : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_BIND_ACK;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->XACT_number = xact;
	p->TOKEN_value = tok;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	printd(("%s: %p: <- TC_BIND_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_SUBS_BIND_ACK
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_subs_bind_ack(queue_t *q, struct tcap *tcap)
{
	int err;
	mblk_t *mp;
	struct TC_subs_bind_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_SUBS_BIND_ACK;
	printd(("%s: %p: <- TC_SUBS_BIND_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_OK_ACK
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_ok_ack(queue_t *q, struct tcap *tcap, ulong prim)
{
	int err;
	mblk_t *mp;
	struct TC_ok_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_OK_ACK;
	p->CORRECT_prim = prim;
	printd(("%s: %p: <- TC_OK_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_ERROR_ACK
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_error_ack(queue_t *q, struct tcap *tcap, ulong prim, long etype, ulong did, ulong iid)
{
	int err;
	mblk_t *mp;
	struct TC_error_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_ERROR_ACK;
	p->ERROR_prim = prim;
	p->TRPI_error = etype < 0 ? TRSYSERR : etype;
	p->UNIX_error = etype < 0 ? -etype : 0;
	p->DIALOG_id = did;
	p->INVOKE_id = iid;
	printd(("%s: %p: <- TC_ERROR_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_OPTMGMT_ACK
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_optmgmt_ack(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, ulong flags)
{
	int err;
	mblk_t *mp;
	struct TC_optmgmt_ack *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_OPTMGMT_ACK;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->MGMT_flags = flags;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	printd(("%s: %p: <- TC_OPTMGMT_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_UNI_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_uni_ind(queue_t *q, struct tcap *tcap, struct sccp_addr *src, struct sccp_addr *dst,
			     struct tcap_opts *opt, ulong did, ulong flags, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TC_uni_ind *p;
	size_t src_len = src ? sizeof(*src) + src->alen : 0;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + src_len + dst_len + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_UNI_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) + src_len : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + src_len + dst_len : 0;
	p->DIALOG_id = did;
	p->COMP_flags = flags;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += src_len;
	}
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TC_UNI_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_BEGIN_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_begin_ind(queue_t *q, struct tcap *tcap, struct sccp_addr *src, struct sccp_addr *dst,
			       struct tcap_opts *opt, ulong did, ulong flags, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TC_begin_ind *p;
	size_t src_len = src ? sizeof(*src) + src->alen : 0;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + src_len + dst_len + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_BEGIN_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) + src_len : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + src_len + dst_len : 0;
	p->DIALOG_id = did;
	p->COMP_flags = flags;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += src_len;
	}
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TC_BEGIN_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_BEGIN_CON
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_begin_con(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, ulong did, ulong flags,
			       mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TC_begin_con *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_BEGIN_CON;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->DIALOG_id = did;
	p->COMP_flags = flags;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TC_BEGIN_CON\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_CONT_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_cont_ind(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, ulong did, ulong flags,
			      mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TC_cont_ind *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_CONT_IND;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->DIALOG_id = did;
	p->COMP_flags = flags;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TC_CONT_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_END_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_end_ind(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, ulong did, ulong flags,
			     mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TC_end_ind *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_END_IND;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->DIALOG_id = did;
	p->COMP_flags = flags;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TC_END_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_ABORT_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_abort_ind(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, ulong did, ulong reason,
			       ulong orig)
{
	int err;
	mblk_t *mp;
	struct TC_abort_ind *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_ABORT_IND;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->DIALOG_id = did;
	p->ABORT_reason = reason;
	p->ORIGINATOR = orig;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	printd(("%s: %p: <- TC_ABORT_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_NOTICE_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_notice_ind(queue_t *q, struct tcap *tcap, ulong did, ulong cause)
{
	int err;
	mblk_t *mp;
	struct TC_notice_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_NOTICE_IND;
	p->DIALOG_id = did;
	p->REPORT_cause = cause;
	printd(("%s: %p: <- TC_NOTICE_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_INVOKE_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_invoke_ind(queue_t *q, struct tcap *tcap, ulong did, ulong ocls, ulong iid, ulong lid,
				ulong oper, ulong more)
{
	int err;
	mblk_t *mp;
	struct TC_invoke_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_INVOKE_IND;
	p->DIALOG_id = did;
	p->OP_class = ocls;
	p->INVOKE_id = iid;
	p->LINKED_id = lid;
	p->OPERATION = oper;
	p->MORE_flags = more;
	printd(("%s: %p: <- TC_INVOKE_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_RESULT_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_result_ind(queue_t *q, struct tcap *tcap, ulong did, ulong iid, ulong oper, ulong more)
{
	int err;
	mblk_t *mp;
	struct TC_result_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_RESULT_IND;
	p->DIALOG_id = did;
	p->INVOKE_id = iid;
	p->OPERATION = oper;
	p->MORE_flag = more;
	printd(("%s: %p: <- TC_RESULT_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_ERROR_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_error_ind(queue_t *q, struct tcap *tcap, ulong did, ulong iid, ulong ecode)
{
	int err;
	mblk_t *mp;
	struct TC_error_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_ERROR_IND;
	p->DIALOG_id = did;
	p->INVOKE_id = iid;
	p->ERROR_code = ecode;
	printd(("%s: %p: <- TC_ERROR_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_CANCEL_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_cancel_ind(queue_t *q, struct tcap *tcap, ulong did, ulong iid)
{
	int err;
	mblk_t *mp;
	struct TC_cancel_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_CANCEL_IND;
	p->DIALOG_id = did;
	p->INVOKE_id = iid;
	printd(("%s: %p: <- TC_CANCEL_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TC_REJECT_IND
 *  -----------------------------------------------------------------
 */
STATIC INLINE int tc_reject_ind(queue_t *q, struct tcap *tcap, ulong did, ulong iid, ulong ecode)
{
	int err;
	mblk_t *mp;
	struct TC_reject_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TC_REJECT_IND;
	p->DIALOG_id = did;
	p->INVOKE_id = iid;
	p->ERROR_code = ecode;
	printd(("%s: %p: <- TC_REJECT_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/* 
 *  TRI Primitives
 *  -------------------------------------------------------------------------
 */
/*
 *  TR_INFO_ACK
 *  -----------------------------------------------------------------
 */
STATIC int tr_info_ack(queue_t *q, struct tcap *tcap)
{
	int err;
	mblk_t *mp;
	struct TR_info_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_INFO_ACK;
	p->TSDU_size = FIXME;
	p->ETSDU_size = FIXME;
	p->CDATA_size = FIXME;
	p->DDATA_size = FIXME;
	p->ADDR_size = sizeof(struct sccp_addr) + SCCP_MAX_ADDR_LEN;
	p->OPT_size = FIXME;
	p->TIDU_size = FIXME;
	p->SERV_type = FIXME;
	p->CURRENT_state = tcap_get_c_state(tcap);
	p->PROVIDER_flag = FIXME;
	p->TRPI_version = FIXME;
	printd(("%s: %p: <- TR_INFO_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_BIND_ACK
 *  -----------------------------------------------------------------
 */
STATIC int tr_bind_ack(queue_t *q, struct tcap *tcap, struct sccp_addr *add, ulong xact, ulong tok)
{
	int err;
	mblk_t *mp;
	struct TR_bind_ack *p;
	size_t add_len = add ? sizeof(*add) + add->alen : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_BIND_ACK;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->XACT_number = xact;
//      p->TOKEN_value = tok;
	p->BIND_flags = flags;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	printd(("%s: %p: <- TR_BIND_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_OK_ACK
 *  -----------------------------------------------------------------
 */
STATIC int tr_ok_ack(queue_t *q, struct tcap *tcap, ulong prim)
{
	int err;
	mblk_t *mp;
	struct TR_ok_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_OK_ACK;
	p->CORRECT_prim = prim;
	printd(("%s: %p: <- TR_OK_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_ERROR_ACK
 *  -----------------------------------------------------------------
 */
STATIC int tr_error_ack(queue_t *q, struct tcap *tcap, ulong prim, long etype, ulong tid)
{
	int err;
	mblk_t *mp;
	struct TR_error_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_ERROR_ACK;
	p->ERROR_prim = prim;
	p->TRPI_error = etype < 0 ? TRSYSERR : etype;
	p->UNIX_error = etype < 0 ? -etype : 0;
	p->TRANS_id = tid;
	printd(("%s: %p: <- TR_ERROR_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_OPTMGMT_ACK
 *  -----------------------------------------------------------------
 */
STATIC int tr_optmgmt_ack(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, ulong flags)
{
	int err;
	mblk_t *mp;
	struct TR_optmgmt_ack *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_OPTMGMT_ACK;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->MGMT_flags = flags;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	printd(("%s: %p: <- TR_OPTMGMT_ACK\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_UNI_IND
 *  -----------------------------------------------------------------
 */
STATIC int tr_uni_ind(queue_t *q, struct tcap *tcap, struct sccp_addr *org, struct sccp_addr *dst,
		      struct tcap_opts *opt, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TR_uni_ind *p;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t org_len = org ? sizeof(*org) + org->alen : 0;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + dst_len + org_len + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_UNI_IND;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->ORIG_length = org_len;
	p->ORIG_offset = org_len ? sizeof(*p) + dst_len : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + dst_len + org_len : 0;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (org_len) {
		bcopy(org, mp->b_wptr, org_len);
		mp->b_wptr += org_len;
	}
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TR_UNI_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_BEGIN_IND
 *  -----------------------------------------------------------------
 */
STATIC int tr_begin_ind(queue_t *q, struct tcap *tcap, struct tr *tr, struct sccp_addr *org,
			struct sccp_addr *dst, struct tcap_opts *opt, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TR_begin_ind *p;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t org_len = org ? sizeof(*org) + org->alen : 0;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + dst_len + org_len + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_BEGIN_IND;
	p->TRANS_id = tr->tid;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->ORIG_length = org_len;
	p->ORIG_offset = org_len ? sizeof(*p) + dst_len : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + dst_len + org_len : 0;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (org_len) {
		bcopy(org, mp->b_wptr, org_len);
		mp->b_wptr += org_len;
	}
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TR_BEGIN_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_BEGIN_CON
 *  -----------------------------------------------------------------
 */
STATIC int tr_begin_con(queue_t *q, struct tcap *tcap, struct tr *tr, struct sccp_addr *org,
			struct tcap_opts *opt, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TR_begin_con *p;
	size_t org_len = org ? sizeof(*org) + org->alen : 0;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + org_len + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_BEGIN_CON;
	p->CORR_id = tr->cid;
	p->TRANS_id = tr->tid;
	p->ORIG_length = org_len;
	p->ORIG_offset = org_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + org_len : 0 if (org_len) {
		bcopy(org, mp->b_wptr, org_len);
		mp->b_wptr += org_len;
	}
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TR_BEGIN_CON\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_CONT_IND
 *  -----------------------------------------------------------------
 */
STATIC int tr_cont_ind(queue_t *q, struct tcap *tcap, struct tr *tr, struct tcap_opts *opt, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TR_cont_ind *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_CONT_IND;
	p->TRANS_id = tr->tid;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TR_CONT_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_END_IND
 *  -----------------------------------------------------------------
 */
STATIC int tr_end_ind(queue_t *q, struct tcap *tcap, struct tr *tr, struct tcap_opts *opt, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct TR_end_ind *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_END_IND;
	p->CORR_id = tr->cid;
	p->TRANS_id = tr->tid;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: <- TR_END_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_ABORT_IND
 *  -----------------------------------------------------------------
 */
STATIC int tr_abort_ind(queue_t *q, struct tcap *tcap, struct tr *tr, ulong cause, ulong orig,
			struct tcap_opts *opt)
{
	int err;
	mblk_t *mp;
	struct TR_abort_ind *p;
	size_t opt_len = tcap_opt_size(tcap, opt);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_ABORT_IND;
	p->ABORT_cause = cause;
	p->ORIGINATOR = orig;
	p->CORR_id = tr->cid;
	p->TRANS_id = tr->tid;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	if (opt_len) {
		tcap_opt_build(tcap, opt, mp->b_wptr);
		mp->b_wptr += opt_len;
	}
	printd(("%s: %p: <- TR_ABORT_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  TR_NOTICE_IND
 *  -----------------------------------------------------------------
 */
STATIC int tr_notice_ind(queue_t *q, struct tcap *tcap, ulong cause, struct tr *tr)
{
	int err;
	mblk_t *mp;
	struct TR_notice_ind *p;
	size_t msg_len = sizeof(*p);
	if (!canput(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = TR_NOTICE_IND;
	p->REPORT_cause = cause;
	p->CORR_id = tr->cid;
	p->TRANS_id = tr->tid;
	printd(("%s: %p: <- TR_NOTICE_IND\n", TCAP_DRV_NAME, tcap));
	ss7_oput(tcap->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/* 
 *  TPI Primitives
 *  -------------------------------------------------------------------------
 */
/* 
 *  T_CONN_IND          11 - connection indication
 *  -----------------------------------------------------------------
 *  We get the information from the CR message in the connection indication.  We queue the CR message (complete with
 *  decode parameter block) itself as the connection indication.  The sequence number is really just a pointer to
 *  the first mblk_t in the received CR message.
 */
STATIC INLINE int t_conn_ind(queue_t *q, struct tcap *tcap, mblk_t *cp)
{
	mblk_t *mp;
	struct tr_msg *m = (typeof(m)) cp->b_rptr;
	struct T_conn_ind *p;
	struct t_opthdr *oh;
	struct tcap_addr *src = &m->cgpa;
	size_t src_len = sizeof(*src) + src->alen;
	size_t opt_len = sizeof(*oh) + sizeof(t_scalar_t);
	size_t msg_len = sizeof(*p) + PAD4(src_len) + opt_len;
	if ((1 << tcap_get_t_state(tcap)) & ~(TSF_IDLE | TSF_WRES_CIND))
		goto efault;
	if (bufq_length(&tcap->conq) >= tcap->conind)
		goto erestart;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_CONN_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + PAD4(src_len) : 0;
	p->SEQ_number = (ulong) cp;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += PAD4(src_len);
	}
	oh = ((typeof(oh)) mp->b_wptr)++;
	oh->len = opt_len;
	oh->level = T_SS7_SCCP;
	oh->name = T_SCCP_PCLASS;
	oh->status = T_SUCCESS;
	*((t_uscalar_t *) mp->b_wptr)++ = m->pclass;
	bufq_queue(&tcap->conq, cp);
	tcap_set_t_state(tcap, TS_WRES_CIND);
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	rare();
	return (-ENOBUFS);
      ebusy:
	rare();
	return (-EBUSY);
      erestart:
	ptrace(("%s: %p: PROTO: too many connection indications\n", TCAP_DRV_NAME, tcap));
	return (-ERESTART);
      efault:
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	return (-EFAULT);
}

/* 
 *  T_CONN_CON          12 - connection confirmation
 *  -----------------------------------------------------------------
 *  The only options with end-to-end significance that are negotiated is the protocol class.
 */
STATIC INLINE int t_conn_con(queue_t *q, struct tcap *tcap, struct sccp_addr *res, ulong pcls, ulong flags,
			     mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct T_conn_con *p;
	struct t_opthdr *oh;
	size_t res_len = res ? sizeof(*res) + res->alen : 0;
	size_t opt_len = sizeof(*oh) + sizeof(t_scalar_t);
	size_t msg_len = sizeof(*p) + PAD4(res_len) + opt_len;
	if (tcap_get_t_state(tcap) != TS_WCON_CREQ)
		goto efault;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;		/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_CONN_CON;
	p->RES_length = res_len;
	p->RES_offset = res_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + PAD4(res_len) : 0;
	if (res_len) {
		bcopy(res, mp->b_wptr, res_len);
		mp->b_wptr += PAD4(res_len);
	}
	oh = ((typeof(oh)) mp->b_wptr)++;
	oh->len = opt_len;
	oh->level = T_SS7_SCCP;
	oh->name = T_SCCP_PCLASS;
	oh->status = T_SUCCESS;
	*((t_uscalar_t *) mp->b_wptr)++ = pcls;
	tcap_set_t_state(tcap, TS_DATA_XFER);
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: no buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      efault:
	err = -EFAULT;
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	goto error;
      error:
	return (err);
}

/* 
 *  T_DISCON_IND        13 - disconnect indication
 *  -----------------------------------------------------------------
 *  We use the address of the mblk_t that contains the CR message as a SEQ_number for connection indications that
 *  are rejected with a disconnect indication as well.  We can use this to directly address the mblk in the
 *  connection indication bufq.
 */
STATIC INLINE int t_discon_ind(queue_t *q, struct tcap *tcap, long reason, mblk_t *seq, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct T_discon_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << tcap->
	     state) & ~(TSF_WCON_CREQ | TSF_WRES_CIND | TSF_DATA_XFER | TSF_WIND_ORDREL | TSF_WREQ_ORDREL))
		goto efault;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_DISCON_IND;
	p->DISCON_reason = reason;
	p->SEQ_number = (ulong) seq;
	if (seq) {
		bufq_unlink(&tcap->conq, seq);
		freemsg(seq);
	}
	if (!bufq_length(&tcap->conq))
		tcap_set_t_state(tcap, TS_IDLE);
	else
		tcap_set_t_state(tcap, TS_WRES_CIND);
	mp->b_cont = dp;
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: no buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      efault:
	err = -EFAULT;
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	goto error;
      error:
	return (err);
}

/* 
 *  T_DATA_IND          14 - data indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_data_ind(queue_t *q, struct tcap *tcap, ulong more, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct T_data_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << tcap_get_t_state(tcap)) & ~(TSF_DATA_XFER | TSF_WIND_ORDREL))
		goto efault;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_DATA_IND;
	p->MORE_flag = more;
	mp->b_cont = dp;
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: no buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      efault:
	err = -EFAULT;
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	goto error;
      error:
	return (err);
}

/* 
 *  T_EXDATA_IND        15 - expedited data indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_exdata_ind(queue_t *q, struct tcap *tcap, ulong more, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct T_exdata_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << tcap_get_t_state(tcap)) & ~(TSF_DATA_XFER | TSF_WIND_ORDREL))
		goto efault;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;		/* expedite */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_EXDATA_IND;
	p->MORE_flag = more;
	mp->b_cont = dp;
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: no buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      efault:
	err = -EFAULT;
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	goto error;
      error:
	return (err);
}

/* 
 *  T_INFO_ACK          16 - information acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_info_ack(queue_t *q, struct tcap *tcap)
{
	int err;
	mblk_t *mp;
	struct T_info_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	{
		ulong serv = tcap->pclass < 2 ? T_CLTS : T_COTS_ORD;
		ulong etsdu = tcap->pclass < 2 ? T_INVALID : tcap->mtu;
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		fixme(("Still some things to double-check here\n"));
		p->PRIM_type = T_INFO_ACK;
		p->TSDU_size = T_INFINITE;	/* no limit on TSDU size */
		p->ETSDU_size = etsdu;	/* no concept of ETSDU size */
		p->CDATA_size = tcap->mtu;	/* no concept of CDATA size */
		p->DDATA_size = tcap->mtu;	/* no concept of DDATA size */
		p->ADDR_size = sizeof(struct tcap_addr);	/* no limit on ADDR size */
		p->OPT_size = T_INFINITE;	/* no limit on OPTIONS size */
		p->TIDU_size = T_INFINITE;	/* no limit on TIDU size */
		p->SERV_type = serv;	/* COTS or CLTS */
		p->CURRENT_state = tcap_get_t_state(tcap);
		p->PROVIDER_flag = XPG4_1 & ~T_SNDZERO;
		putnext(tcap->oq, mp);
		return (0);
	}
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_BIND_ACK          17 - bind acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_bind_ack(queue_t *q, struct tcap *tcap, struct tcap_addr *add)
{
	int err;
	mblk_t *mp;
	struct T_bind_ack *p;
	size_t add_len = add ? sizeof(*add) : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (tcap_get_t_state(tcap) != TS_WACK_BREQ)
		goto efault;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_BIND_ACK;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->CONIND_number = tcap->conind;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	tcap_set_t_state(tcap, TS_IDLE);
	putnext(tcap->oq, mp);
	return (0);
      efault:
	err = -EFAULT;
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	goto error;
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_ERROR_ACK         18 - error acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_error_ack(queue_t *q, struct tcap *tcap, const ulong prim, long error)
{
	int err = error;
	mblk_t *mp;
	struct T_error_ack *p;
	size_t msg_len = sizeof(*p);
	switch (error) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		goto error;
	case 0:
		never();
		goto error;
	}
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_ERROR_ACK;
	p->ERROR_prim = prim;
	p->TLI_error = error < 0 ? TSYSERR : error;
	p->UNIX_error = error < 0 ? -error : 0;
	switch (tcap_get_t_state(tcap)) {
#ifdef TS_WACK_OPTREQ
	case TS_WACK_OPTREQ:
#endif
	case TS_WACK_UREQ:
	case TS_WACK_CREQ:
		tcap_set_t_state(tcap, TS_IDLE);
		break;
	case TS_WACK_BREQ:
		tcap_set_t_state(tcap, TS_UNBND);
		break;
	case TS_WACK_CRES:
		tcap_set_t_state(tcap, TS_WRES_CIND);
		break;
	case TS_WACK_DREQ6:
		tcap_set_t_state(tcap, TS_WCON_CREQ);
		break;
	case TS_WACK_DREQ7:
		tcap_set_t_state(tcap, TS_WRES_CIND);
		break;
	case TS_WACK_DREQ9:
		tcap_set_t_state(tcap, TS_DATA_XFER);
		break;
	case TS_WACK_DREQ10:
		tcap_set_t_state(tcap, TS_WIND_ORDREL);
		break;
	case TS_WACK_DREQ11:
		tcap_set_t_state(tcap, TS_WREQ_ORDREL);
		break;
		/* Note: if we are not in a WACK state we simply do not change state.  This occurs normally when
		   we send TOUTSTATE or TNOTSUPPORT or are responding to a T_OPTMGMT_REQ in other then TS_IDLE
		   state. */
	}
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_OK_ACK            19 - success acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_ok_ack(queue_t *q, struct tcap *tcap, ulong prim, ulong seq, ulong tok)
{
	int err;
	mblk_t *mp;
	struct T_ok_ack *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_OK_ACK;
	p->CORRECT_prim = prim;
	switch (tcap_get_t_state(tcap)) {
	case TS_WACK_CREQ:
		tcap_set_t_state(tcap, TS_WCON_CREQ);
		break;
	case TS_WACK_UREQ:
		tcap_set_t_state(tcap, TS_UNBND);
		break;
	case TS_WACK_CRES:
	{
		queue_t *aq = (queue_t *) tok;
		struct tcap *ap = SCCP_PRIV(aq);
		if (ap) {
			ap->i_state = TS_DATA_XFER;
			tcap_cleanup_read(q);
			tcap_transmit_wakeup(q);
		}
		if (seq) {
			bufq_unlink(&tcap->conq, (mblk_t *) seq);
			freemsg((mblk_t *) seq);
		}
		if (aq != tcap->oq) {
			if (bufq_length(&tcap->conq))
				tcap_set_t_state(tcap, TS_WRES_CIND);
			else
				tcap_set_t_state(tcap, TS_IDLE);
		}
		break;
	}
	case TS_WACK_DREQ7:
		if (seq)
			bufq_unlink(&tcap->conq, (mblk_t *) seq);
	case TS_WACK_DREQ6:
	case TS_WACK_DREQ9:
	case TS_WACK_DREQ10:
	case TS_WACK_DREQ11:
		if (bufq_length(&tcap->conq))
			tcap_set_t_state(tcap, TS_WRES_CIND);
		else
			tcap_set_t_state(tcap, TS_IDLE);
		break;
		/* Note: if we are not in a WACK state we simply do not change state.  This occurs normally when
		   we are responding to a T_OPTMGMT_REQ in other than the TS_IDLE state. */
	}
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_OPTMGMT_ACK       22 - options management acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_optmgmt_ack(queue_t *q, struct tcap *tcap, ulong flags, struct tcap_opts *ops)
{
	int err;
	mblk_t *mp;
	struct T_optmgmt_ack *p;
	size_t opt_len = tcap_opts_size(tcap, ops);
	size_t msg_len = sizeof(*p) + opt_len;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_OPTMGMT_ACK;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) : 0;
	p->MGMT_flags = flags;
	tcap_build_opts(tcap, ops, mp->b_wptr);
	mp->b_wptr += opt_len;
#ifdef TS_WACK_OPTREQ
	if (tcap_get_t_state(tcap) == TS_WACK_OPTREQ)
		tcap_set_t_state(tcap, TS_IDLE);
#endif
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_ORDREL_IND        23 - orderly release indication
 *  -----------------------------------------------------------------
 */
STATIC INLINE int t_ordrel_ind(queue_t *q, struct tcap *tcap)
{
	int err;
	mblk_t *mp;
	struct T_ordrel_ind *p;
	size_t msg_len = sizeof(*p);
	if ((1 << tcap_get_t_state(tcap)) & ~(TSF_DATA_XFER | TSF_WIND_ORDREL))
		goto efault;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_ORDREL_IND;
	switch (tcap_get_t_state(tcap)) {
	case TS_DATA_XFER:
		tcap_set_t_state(tcap, TS_WREQ_ORDREL);
		break;
	case TS_WIND_ORDREL:
		tcap_set_t_state(tcap, TS_IDLE);
		break;
	}
	putnext(tcap->oq, mp);
	return (0);
      efault:
	err = -EFAULT;
	pswerr(("%s: %p: SWERR: unexpected indication for state %ld\n", TCAP_DRV_NAME, tcap,
		tcap_get_t_state(tcap)));
	goto error;
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_ADDR_ACK          27 - address acknowledgement
 *  -----------------------------------------------------------------
 */
STATIC int t_addr_ack(queue_t *q, struct tcap *tcap, struct tcap_addr *loc, struct tcap_addr *rem)
{
	int err;
	mblk_t *mp;
	struct T_addr_ack *p;
	size_t loc_len = loc ? sizeof(*loc) : 0;
	size_t rem_len = rem ? sizeof(*rem) : 0;
	size_t msg_len = sizeof(*p) + loc_len + rem_len;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = ((struct T_addr_ack *) mp->b_wptr)++;
	p->PRIM_type = T_ADDR_ACK;
	p->LOCADDR_length = loc_len;
	p->LOCADDR_offset = loc_len ? sizeof(*p) : 0;
	p->REMADDR_length = rem_len;
	p->REMADDR_offset = rem_len ? sizeof(*p) + loc_len : 0;
	if (loc_len) {
		bcopy(loc, mp->b_wptr, loc_len);
		mp->b_wptr += loc_len;
	}
	if (rem_len) {
		bcopy(rem, mp->b_wptr, rem_len);
		mp->b_wptr += rem_len;
	}
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

#if 0
/* 
 *  T_CAPABILITY_ACK    ?? - protocol capability ack
 *  -----------------------------------------------------------------
 */
STATIC int t_capability_ack(queue_t *q, struct tcap *tcap, ulong caps)
{
	int err;
	mblk_t *mp;
	struct T_capability_ack *p;
	size_t msg_len = sizeof(*p);
	ulong caps = (acceptor ? TC1_ACCEPTOR : 0) | (info ? TC1_INFO : 0);
	if ((mp = ss7_allocb(q, msg_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((struct T_capability_ack *) mp->b_wptr)++;
		p->PRIM_type = T_CAPABILITY_ACK;
		p->CAP_bits1 = caps;
		p->ACCEPTOR_id = (caps & TC1_ACCEPTOR) ? (ulong) tcap->oq : 0;
		if (caps & TC1_INFO) {
			p->INFO_ack.PRIM_type = T_INFO_ACK;
			p->INFO_ack.TSDU_size = tcap->tsdu;
			p->INFO_ack.ETSDU_size = tcap->etsdu;
			p->INFO_ack.CDATA_size = tcap->cdata;
			p->INFO_ack.DDATA_size = tcap->ddata;
			p->INFO_ack.ADDR_size = tcap->addlen;
			p->INFO_ack.OPT_size = tcap->optlen;
			p->INFO_ack.TIDU_size = tcap->tidu;
			p->INFO_ack.SERV_type = tcap->stype;
			p->INFO_ack.CURRENT_state = tcap_get_t_state(tcap);
			p->INFO_ack.PROVIDER_flag = tcap->ptype;
		} else
			bzero(&p->INFO_ack, sizeof(p->INFO_ack));
		putnext(tcap->oq, mp);
		return (0);
	} else {
		err = -ENOBUFS;
		ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	}
	return (err);
}
#endif

/* 
 *  T_UNITDATA_IND      20 - Unitdata indication
 *  -----------------------------------------------------------------
 *  Note: If we cannot deliver the destination address in the option header or somewhere, it will not be possible to
 *  bind to multiple alias addresses, but will only permit us to bind to a single alias address.  This might or
 *  might not be a problem to the user.
 */
STATIC INLINE int t_unitdata_ind(queue_t *q, struct tcap *tcap, struct tcap_addr *src, ulong * seq, ulong * prior,
				 ulong * pcls, ulong * imp, ulong * rerr, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct t_opthdr *oh;
	const size_t olen = sizeof(*oh) + sizeof(t_scalar_t);
	struct T_unitdata_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len =
	    (seq ? olen : 0) + (prior ? olen : 0) + (pcls ? olen : 0) + (imp ? olen : 0) + (rerr ? olen : 0);
	size_t msg_len = sizeof(*p) + src_len + opt_len;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_UNITDATA_IND;
	p->SRC_length = src_len;
	p->SRC_offset = src_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + src_len : 0;
	if (src_len) {
		bcopy(src, mp->b_wptr, src_len);
		mp->b_wptr += src_len;
	}
	if (opt_len) {
		if (seq) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_SEQ_CTRL;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *seq;
		}
		if (prior) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_PRIORITY;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *prior;
		}
		if (pcls) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_PCLASS;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *pcls;
		}
		if (imp) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_IMPORTANCE;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *imp;
		}
		if (rerr) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_RET_ERROR;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *rerr;
		}
	}
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: Flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  T_UDERROR_IND       21 - Unitdata error indication
 *  -----------------------------------------------------------------
 *  This primitive indicates to the transport user that a datagram with the specified destination address and
 *  options produced an error.
 */
STATIC INLINE int t_uderror_ind(queue_t *q, struct tcap *tcap, ulong etype, struct tcap_addr *dst, ulong * seq,
				ulong * pri, ulong * pcl, ulong * imp, ulong * ret, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	struct t_opthdr *oh;
	const size_t olen = sizeof(*oh) + sizeof(t_scalar_t);
	struct T_uderror_ind *p;
	size_t dst_len = dst ? sizeof(*dst) : 0;
	size_t opt_len =
	    (seq ? olen : 0) + (pri ? olen : 0) + (pcl ? olen : 0) + (imp ? olen : 0) + (ret ? olen : 0);
	size_t msg_len = sizeof(*p) + dst_len;
	if (!canputnext(tcap->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 2;		/* XXX move ahead of data indications */
	p = ((typeof(p)) mp->b_wptr)++;
	p->PRIM_type = T_UDERROR_IND;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->OPT_length = opt_len;
	p->OPT_offset = opt_len ? sizeof(*p) + dst_len : 0;
	p->ERROR_type = etype;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (opt_len) {
		if (seq) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_SEQ_CTRL;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *seq;
		}
		if (pri) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_PRIORITY;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *pri;
		}
		if (pcl) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_PCLASS;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *pcl;
		}
		if (imp) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_IMPORTANCE;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *imp;
		}
		if (ret) {
			oh = ((typeof(oh)) mp->b_wptr)++;
			oh->len = olen;
			oh->level = T_SS7_SCCP;
			oh->name = T_SCCP_RET_ERROR;
			oh->status = T_SUCCESS;
			*((t_uscalar_t *) mp->b_wptr)++ = *ret;
		}
	}
	putnext(tcap->oq, mp);
	return (0);
      enobufs:
	err = -ENOBUFS;
	ptrace(("%s: %p: ERROR: No buffers\n", TCAP_DRV_NAME, tcap));
	goto error;
      ebusy:
	err = -EBUSY;
	ptrace(("%s: %p: ERROR: Flow controlled\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return (err);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Primitives sent downstream (to SCCP)
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  N_CONN_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_conn_req(queue_t *q, struct sccp *sccp, struct sccp_addr *dst, ulong flags,
			     N_qos_sel_conn_sccp_t * qos, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	N_conn_req_t *p;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t qos_len = qos ? sizeof(*qos) : 0;
	size_t msg_len = sizeof(*p) + dst_len + qos_len;
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_IDLE))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_CONN_REQ;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->CONN_flags = flags;
	p->OPT_length = qos_len;
	p->OPT_offset = qos_len ? sizeof(*p) + dst_len : 0;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	if (qos_len) {
		bcopy(qos, mp->b_wptr, qos_len);
		mp->b_wptr += qos_len;
	}
	mp->b_cont = dp;
	sccp_set_i_state(sccp, NS_WCON_CREQ);
	printd(("%s: %p: N_CONN_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_CONN_RES
 *  -----------------------------------
 */
STATIC INLINE int n_conn_res(queue_t *q, struct sccp *sccp, ulong tok, struct sccp_addr *res, ulong seq,
			     ulong flags, N_qos_sel_conn_sccp_t * qos, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	N_conn_res_t *p;
	size_t res_len = res ? sizeof(*res) + res->alen : 0;
	size_t qos_len = qos ? sizeof(*qos) : 0;
	size_t msg_len = sizeof(*p) + res_len + qos_len;
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_WRES_CIND))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_CONN_RES;
	p->TOKEN_value = tok;
	p->RES_length = res_len;
	p->RES_offset = res_len ? sizeof(*p) : 0;
	p->SEQ_number = seq;
	p->CONN_flags = flags;
	p->OPT_length = qos_len;
	p->OPT_offset = qos_len ? sizeof(*p) + res_len : 0;
	if (res_len) {
		bcopy(res, mp->b_wptr, res_len);
		mp->b_wptr += res_len;
	}
	if (qos_len) {
		bcopy(qos, mp->b_wptr, qos_len);
		mp->b_wptr += qos_len;
	}
	mp->b_cont = dp;
	if (--sccp->outcnt)
		sccp_set_i_state(sccp, NS_IDLE);
	else
		sccp_set_i_state(sccp, NS_WRES_CIND);
	printd(("%s: %p: N_CONN_RES ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_DISCON_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_discon_req(queue_t *q, struct sccp *sccp, ulong reason, struct sccp_addr *res, ulong seq,
			       mblk_t *dp)
{
	int err;
	mblk_t *mp;
	N_discon_req_t *p;
	size_t res_len = res ? sizeof(*res) + res->alen : 0;
	size_t msg_len = sizeof(*p) + res_len;
	if ((1 << sccp_get_i_state(sccp)) &
	    ~(NSF_WCON_CREQ | NSF_WRES_CIND | NSF_DATA_XFER | NSF_WCON_CREQ | NSF_WRES_RIND))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_DISCON_REQ;
	p->DISCON_reason = reason;
	p->RES_length = res_len;
	p->RES_offset = res_len ? sizeof(*p) : 0;
	p->SEQ_number = seq;
	if (res_len) {
		bcopy(res, mp->b_wptr, res_len);
		mp->b_wptr += res_len;
	}
	mp->b_cont = dp;
	switch (sccp_get_i_state(sccp)) {
	case NS_WCON_CREQ:
		sccp_set_i_state(sccp, NS_WACK_DREQ6);
		break;
	case NS_WRES_CIND:
		sccp_set_i_state(sccp, NS_WACK_DREQ7);
		break;
	case NS_DATA_XFER:
		sccp_set_i_state(sccp, NS_WACK_DREQ9);
		break;
	case NS_WCON_RREQ:
		sccp_set_i_state(sccp, NS_WACK_DREQ10);
		break;
	case NS_WRES_RIND:
		sccp_set_i_state(sccp, NS_WACK_DREQ11);
		break;
	default:
		swerr();
		break;
	}
	p->printd(("%s: %p: N_DISCON_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_DATA_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_data_req(queue_t *q, struct sccp *sccp, ulong flags, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	N_data_req_t *p;
	size_t msg_len = sizeof(*p);
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_DATA_REQ;
	p->DATA_xfer_flags = flags;
	mp->b_cont = dp;
	printd(("%s: %p: N_DATA_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      efault:
	if (sccp_get_i_state(sccp) == NS_IDLE)
		goto discard;
	err = -EFAULT;
	swerr();
	goto error;
      discard:
	err = QR_DONE;
	rare();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_EXDATA_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_exdata_req(queue_t *q, struct sccp *sccp, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	N_exdata_req_t *p;
	size_t msg_len = sizeof(*p);
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	mp->b_band = 1;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_EXDATA_REQ;
	mp->b_cont = dp;
	printd(("%s: %p: N_EXDATA_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      efault:
	if (sccp_get_i_state(sccp) == NS_IDLE)
		goto discard;
	err = -EFAULT;
	swerr();
	goto error;
      discard:
	err = QR_DONE;
	rare();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_INFO_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_info_req(queue_t *q, struct sccp *sccp)
{
	int err;
	mblk_t *mp;
	N_info_req_t *p;
	size_t msg_len = sizeof(*p);
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_INFO_REQ;
	printd(("%s: %p: N_INFO_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_BIND_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_bind_req(queue_t *q, struct sccp *sccp, struct sccp_addr *add, ulong cons, ulong flags,
			     uchar *pro, size_t pro_len)
{
	int err;
	mblk_t *mp;
	N_bind_req_t *p;
	size_t add_len = add ? sizeof(*add) + add->alen : 0;
	size_t msg_len = sizeof(*p) + add_len + pro_len;
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_UNBND))
		goto efault;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_BIND_REQ;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->CONIND_number = cons;
	p->BIND_flags = flags;
	p->PROTOID_length = pro_len;
	p->PROTOID_offset = pro_len ? sizeof(*p) + add_len : 0;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	if (pro_len) {
		bcopy(pro, mp->b_wptr, pro_len);
		mp->b_wptr += pro_len;
	}
	sccp_set_i_state(sccp, NS_WACK_BREQ);
	printd(("%s: %p: N_BIND_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_UNBIND_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_unbind_req(queue_t *q, struct sccp *sccp)
{
	int err;
	mblk_t *mp;
	N_unbind_req_t *p;
	size_t msg_len = sizeof(*p);
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_IDLE))
		goto efault;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PCPROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_UNBIND_REQ;
	sccp_set_i_state(sccp, NS_WACK_UREQ);
	printd(("%s: %p: N_UNBIND_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_UNITDATA_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_unitdata_req(queue_t *q, struct sccp *sccp, struct sccp_addr *dst, mblk_t *dp)
{
	int err;
	mblk_t *mp;
	N_unitdata_req_t *p;
	size_t dst_len = dst ? sizeof(*dst) + dst->alen : 0;
	size_t msg_len = sizeof(*p) + dst_len;
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_IDLE))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_UNITDATA_REQ;
	p->DEST_length = dst_len;
	p->DEST_offset = dst_len ? sizeof(*p) : 0;
	p->RESERVED_field[0] = 0;
	p->RESERVED_field[1] = 0;
	if (dst_len) {
		bcopy(dst, mp->b_wptr, dst_len);
		mp->b_wptr += dst_len;
	}
	mp->b_cont = dp;
	printd(("%s: %p: N_UNITDATA_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_OPTMGMT_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_optmgmt_req(queue_t *q, struct sccp *sccp, N_qos_sel_info_sccp_t * qos, ulong flags)
{
	int err;
	mblk_t *mp;
	N_optmgmt_req_t *p;
	size_t qos_len = qos ? sizeof(*qos) : 0;
	size_t msg_len = sizeof(*p) + qos_len;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_OPTMGMT_REQ;
	p->OPT_length = qos_len;
	p->OPT_offset = qos_len ? sizeof(*p) : 0;
	p->OPTMGMT_flags = flags;
	if (sccp_get_i_state(sccp) == NS_IDLE)
		sccp_set_i_state(sccp, NS_WACK_OPTREQ);
	printd(("%s: %p: N_OPTMGMT_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_DATACK_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_datack_req(queue_t *q, struct sccp *sccp)
{
	int err;
	mblk_t *mp;
	N_datack_req_t *p;
	size_t msg_len = sizeof(*p);
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_DATACK_REQ;
	printd(("%s: %p: N_DATACK_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      efault:
	if (sccp_get_i_state(sccp) == NS_IDLE)
		goto discard;
	err = -EFAULT;
	swerr();
	goto error;
      discard:
	err = QR_DONE;
	rare();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_RESET_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_reset_req(queue_t *q, struct sccp *sccp, ulong reason)
{
	int err;
	mblk_t *mp;
	N_reset_req_t *p;
	size_t msg_len = sizeof(*p);
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_RESET_REQ;
	p->RESET_reason = reason;
	sccp_set_i_state(sccp, NS_WCON_RREQ);
	printd(("%s: %p: N_RESET_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_RESET_RES
 *  -----------------------------------
 */
STATIC INLINE int n_reset_res(queue_t *q, struct sccp *sccp)
{
	int err;
	mblk_t *mp;
	N_reset_res_t *p;
	size_t msg_len = sizeof(*p);
	if ((1 << sccp_get_i_state(sccp)) & ~(NSF_DATA_XFER))
		goto efault;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_RESET_RES;
	sccp_set_i_state(sccp, NS_DATA_XFER);
	printd(("%s: %p: N_RESET_RES ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      efault:
	err = -EFAULT;
	swerr();
	goto error;
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_INFORM_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_inform_req(queue_t *q, struct sccp *sccp)
{
	int err;
	mblk_t *mp;
	N_inform_req_t *p;
	size_t msg_len = sizeof(*p);
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_INFORM_REQ;
	printd(("%s: %p: N_INFORM_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_ABSORBED);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_COORD_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_coord_req(queue_t *q, struct sccp *sccp, struct sccp_addr *add)
{
	int err;
	mblk_t *mp;
	N_coord_req_t *p;
	size_t add_len = add ? sizeof(*add) + add->alen : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_COORD_REQ;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	printd(("%s: %p: N_COORD_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_COORD_RES
 *  -----------------------------------
 */
STATIC INLINE int n_coord_res(queue_t *q, struct sccp *sccp, struct sccp_addr *add)
{
	int err;
	mblk_t *mp;
	N_coord_res_t *p;
	size_t add_len = add ? sizeof(*add) + add->alen : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_COORD_RES;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	printd(("%s: %p: N_COORD_RES ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  N_STATE_REQ
 *  -----------------------------------
 */
STATIC INLINE int n_state_req(queue_t *q, struct sccp *sccp, struct sccp_addr *add, ulong status)
{
	int err;
	mblk_t *mp;
	N_state_req_t *p;
	size_t add_len = add ? sizeof(*add) + add->alen : 0;
	size_t msg_len = sizeof(*p) + add_len;
	if (!canput(sccp->oq))
		goto ebusy;
	if (!(mp = ss7_allocb(q, msg_len, BPRI_MED)))
		goto enobufs;
	mp->b_datap->db_type = M_PROTO;
	p = (typeof(p)) mp->b_wptr++;
	p->PRIM_type = N_STATE_REQ;
	p->ADDR_length = add_len;
	p->ADDR_offset = add_len ? sizeof(*p) : 0;
	p->STATUS = status;
	if (add_len) {
		bcopy(add, mp->b_wptr, add_len);
		mp->b_wptr += add_len;
	}
	printd(("%s: %p: N_STATE_REQ ->\n", TCAP_DRV_NAME, sccp));
	ss7_oput(sccp->oq, mp);
	return (QR_DONE);
      enobufs:
	err = -ENOBUFS;
	rare();
	goto error;
      ebusy:
	err = -EBUSY;
	rare();
	goto error;
      error:
	return (err);
}

/*
 *  =========================================================================
 *
 *  TCAP SEND Message Functions
 *
 *  =========================================================================
 */

/*
 *  TCAP_MT_UNI
 *  -----------------------------------
 */
STATIC int tcap_send_uni(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

/*
 *  TCAP_MT_QWP
 *  -----------------------------------
 */
STATIC int tcap_send_qwp(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

/*
 *  TCAP_MT_QWOP
 *  -----------------------------------
 */
STATIC int tcap_send_qwop(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

/*
 *  TCAP_MT_CWP
 *  -----------------------------------
 */
STATIC int tcap_send_cwp(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

/*
 *  TCAP_MT_CWOP
 *  -----------------------------------
 */
STATIC int tcap_send_cwop(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

/*
 *  TCAP_MT_RESP
 *  -----------------------------------
 */
STATIC int tcap_send_resp(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

/*
 *  TCAP_MT_ABORT
 *  -----------------------------------
 */
STATIC int tcap_send_abort(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
}

STATIC int tcap_send_msg(queue_t *q, struct sccp *sccp, struct tr_msg *m)
{
	int ret;
	switch (m->type) {
	case TCAP_MT_UNI:
		ret = tcap_send_uni(q, sccp, m);
		break;
	case TCAP_MT_QWP:
		ret = tcap_send_qwp(q, sccp, m);
		break;
	case TCAP_MT_QWOP:
		ret = tcap_send_qwop(q, sccp, m);
		break;
	case TCAP_MT_CWP:
		ret = tcap_send_cwp(q, sccp, m);
		break;
	case TCAP_MT_CWOP:
		ret = tcap_send_cwop(q, sccp, m);
		break;
	case TCAP_MT_RESP:
		ret = tcap_send_resp(q, sccp, m);
		break;
	case TCAP_MT_ABORT:
		ret = tcap_send_abort(q, sccp, m);
		break;
	default:
		ret = -EFAULT;
		break;
	}
	return (ret);
}

/*
 *  =========================================================================
 *
 *  TCAP RECV Message Functions
 *
 *  =========================================================================
 */
/*
 *  TCAP_MT_UNI
 *  -----------------------------------
 */
STATIC int tcap_recv_uni(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	int err;
	ulong did = 0;
	ulong flags = (m->parms & TCAP_PTF_CSEQ) ? 1 : 0;
	todo(("Fill out other options\n"));
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_uni_ind(q, tcap, &m->sccp.orig, &m->sccp.dest, opt, dp)) < 0)
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_uni_ind(q, tcap, &m->sccp.orig, &m->sccp.dest, opt, did, flags, dp)) < 0)
			goto error;
		/* Provide a TC_UNI_IND and follow with any TC_XXX_IND component indications. */
		fixme(("Send components.\n"));
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_unitdata_ind(q, tcap, &m->sccp.orig, opt, dp)) < 0)
			goto error;
		break;
	default:
		err = -EFAULT;
		swerr();
		goto error;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

/*
 *  TCAP_MT_QWP
 *  -----------------------------------
 */
STATIC int tcap_recv_qwp(queue_t *q, struct tcap *tcap, struct tcap_opt *opt, struct tr_msg *m)
{
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_begin_ind(q, tcap, tr, &m->sccp.orig, &m->sccp.dest, opt, dp)) < 0)
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_begin_ind(q, tcap, &m->sccp.orig, &m->sccp.dest, opt, did, flags, dp)) < 0)
			goto error;
		/* Provide a TC_BEG_IND and follow with any TC_XXX_IND component indications. */
		fixme(("Send components\n"));
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_conn_ind(q, tcap, m->bp)) < 0)
			goto error;
		break;
	default:
		err = -EFAULT;
		swerr();
		goto error;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

/*
 *  TCAP_MT_QWOP
 *  -----------------------------------
 */
STATIC int tcap_recv_qwop(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_begin_ind(q, tcap, tr, &m->sccp.orig, &m->sccp.dest, opt, dp)) < 0)
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_begin_ind(q, tcap, &m->sccp.orig, &m->sccp.dest, opt, did, flags, dp)) < 0)
			goto error;
		/* Provide a TC_BEG_IND and follow with any TC_XXX_IND component indications. */
		fixme(("Send components\n"));
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_conn_ind(q, tcap, m->bp)) < 0)
			goto error;
		break;
	default:
		err = -EFAULT;
		swerr();
		goto error;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

/*
 *  TCAP_MT_CWP
 *  -----------------------------------
 */
STATIC int tcap_recv_cwp(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_begin_con(q, tcap, tr, &m->sccp.orig, opt, dp)))
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_begin_con(q, tcap, opt, did, flags, dp)))
			goto error;
		/* Provide a TC_BEG_CON or TC_CONT_IND and follow with any TC_XXX_IND component indications. */
		fixme(("Send components.\n"));
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_conn_con(q, tcap, &m->sccp.orig, opt, flags, dp)))
			goto error;
		break;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

/*
 *  TCAP_MT_CWOP
 *  -----------------------------------
 */
STATIC int tcap_recv_cwop(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_begin_con(q, tcap, tr, &m->sccp.orig, opt, dp)))
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_begin_con(q, tcap, opt, did, flags, dp)))
			goto error;
		/* Provide a TC_BEG_CON or TC_CONT_IND and follow with any TC_XXX_IND component indications. */
		fixme(("Send components.\n"));
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_conn_con(q, tcap, &m->sccp.orig, opt, flags, dp)))
			goto error;
		break;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

/*
 *  TCAP_MT_RESP
 *  -----------------------------------
 */
STATIC int tcap_recv_resp(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_end_ind(q, tcap, tr, opt, dp)))
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_end_ind(q, tcap, opt, did, flags, dp)))
			goto error;
		/* Provide a TC_END_IND and follow with any TC_XXX_IND component indications. */
		fixme(("Send components.\n"));
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_ordrel_ind(q, tcap)))
			goto error;
		break;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

/*
 *  TCAP_MT_ABORT
 *  -----------------------------------
 */
STATIC int tcap_recv_abort(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		if ((err = tr_abort_ind(q, tcap, tr, cause, orig, opt)))
			goto error;
		break;
	case TCAP_STYLE_TCI:
		if ((err = tc_abort_ind(q, tcap, opt, did, reason, orig)))
			goto error;
		fixme(("Send components.\n"));
		/* Provide a TC_ABORT_IND. */
		break;
	case TCAP_STYLE_TPI:
		if ((err = t_discon_ind(q, tcap, reason, seq, dp)))
			goto error;
		break;
	}
	return (QR_TRIMMED);
      error:
	return (err);
}

STATIC int tcap_recv_msg(queue_t *q, struct tcap *tcap, struct tcap_opts *opt, struct tr_msg *m)
{
	int ret;
	switch (m->type) {
	case TCAP_MT_UNI:
		ret = tcap_recv_uni(q, tcap, opt, m);
		break;
	case TCAP_MT_QWP:
		ret = tcap_recv_qwp(q, tcap, opt, m);
		break;
	case TCAP_MT_QWOP:
		ret = tcap_recv_qwop(q, tcap, opt, m);
		break;
	case TCAP_MT_CWP:
		ret = tcap_recv_cwp(q, tcap, opt, m);
		break;
	case TCAP_MT_CWOP:
		ret = tcap_recv_cwop(q, tcap, opt, m);
		break;
	case TCAP_MT_RESP:
		ret = tcap_recv_resp(q, tcap, opt, m);
		break;
	case TCAP_MT_ABORT:
		ret = tcap_recv_abort(q, tcap, opt, m);
		break;
	default:
		ret = -EOPNOTSUPP;
		break;
	}
	return (ret);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Received Primitives
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  Primitives received from above (TCAP User).
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  TCI Primitives
 *  -----------------------------------------------------------
 */
/*
 *  TC_INFO_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_info_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_info_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, 0, 0);
}

/*
 *  TC_BIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_bind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_bind_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add = NULL;
	ulong xact = 0, flags = 0;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->ADDR_length) {
		if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
			goto badprim;
		if (p->ADDR_length < sizeof(*add))
			goto badaddr;
		add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
		if (p->ADDR_length != sizeof(*add) + add->alen)
			goto badaddr;
	}
	xact = p->XACT_number;
	flags = p->BIND_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	err = TCBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, 0, 0);
}

/*
 *  TC_UNBIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_unbind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_unbind_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (tcap->dg.list)
		goto outstate;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      outstate:
	err = TCOUTSTATE;
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, 0, 0);
}

/*
 *  TC_SUBS_BIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_subs_bind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_subs_bind_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, 0, 0);
}

/*
 *  TC_SUBS_UNBIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_subs_unbind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_subs_unbind_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, 0, 0);
}

/*
 *  TC_OPTMGMT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_optmgmt_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_optmgmt_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, NULL, };
	ulong flags = 0;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	flags = p->MGMT_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, 0, 0);
}

/*
 *  TC_UNI_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_uni_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_uni_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *src = NULL;
	struct sccp_addr *dst = NULL;
	struct tcap_opts opts = { 0UL, };
	struct dg *dg;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->SRC_length) {
		if (mp->b_wptr < mp->b_rptr + p->SRC_offset + p->SRC_length)
			goto badprim;
		if (p->SRC_length < sizeof(*src))
			goto badaddr;
		src = (typeof(src)) (mp->b_rptr + p->SRC_offset);
		if (p->SRC_length != sizeof(*src) + src->alen)
			goto badaddr;
	}
	if (p->DEST_length) {
		if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
			goto badprim;
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length != sizeof(*dst) + dst->alen)
			goto badaddr;
	}
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TCBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, 0);
}

/*
 *  TC_BEGIN_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_begin_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_begin_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *src = NULL;
	struct sccp_addr *dst = NULL;
	struct tcap_opts opts = { 0UL, };
	ulong flags;
	struct dg *dg;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->SRC_length) {
		if (mp->b_wptr < mp->b_rptr + p->SRC_offset + p->SRC_length)
			goto badprim;
		if (p->SRC_length < sizeof(*src))
			goto badaddr;
		src = (typeof(src)) (mp->b_rptr + p->SRC_offset);
		if (p->SRC_length != sizeof(*src) + src->alen)
			goto badaddr;
	}
	if (p->DEST_length) {
		if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
			goto badprim;
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length != sizeof(*dst) + dst->alen)
			goto badaddr;
	}
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	flags = p->COMP_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TCBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, 0);
}

/*
 *  TC_BEGIN_RES
 *  -----------------------------------------------------------
 */
STATIC int tc_begin_res(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_begin_res *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *src = NULL;
	struct tcap_opts opts = { 0UL, };
	ulong flags;
	struct dg *dg;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->SRC_length) {
		if (mp->b_wptr < mp->b_rptr + p->SRC_offset + p->SRC_length)
			goto badprim;
		if (p->SRC_length < sizeof(*src))
			goto badaddr;
		src = (typeof(src)) (mp->b_rptr + p->SRC_offset);
		if (p->SRC_length != sizeof(*src) + src->alen)
			goto badaddr;
	}
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	flags = p->COMP_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TCBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, 0);
}

/*
 *  TC_CONT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_cont_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_cont_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, };
	ulong flags;
	struct dg *dg;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	flags = p->COMP_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, 0);
}

/*
 *  TC_END_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_end_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_end_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, };
	ulong scenario;
	struct dg *dg;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	scenario = p->TERM_scenario;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, 0);
}

/*
 *  TC_ABORT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_abort_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_abort_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, };
	ulong reason;
	struct dg *dg;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	reason = p->ABORT_reason;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TCBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, 0);
}

/*
 *  TC_INVOKE_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_invoke_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_invoke_req *p = (typeof(p)) mp->b_rptr;
	ulong pcls, oper, more, to;
	struct dg *dg;
	struct iv *iv, *li;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	if (p->LINKED_id && !(li = iv_lookup(dg, p->LINKED_id)))
		goto badlid;
	if (!(iv = iv_lookup(dg, p->INVOKE_id)))
		goto badiid;
	pcls = p->PROTOCOL_class;
	oper = p->OPERATION;
	more = p->MORE_flag;
	to = p->TIMEOUT;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, p->INVOKE_id);
}

/*
 *  TC_RESULT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_result_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_result_req *p = (typeof(p)) mp->b_rptr;
	ulong oper, more;
	struct dg *dg;
	struct iv *iv;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	if (!(iv = iv_lookup(dg, p->INVOKE_id)))
		goto badiid;
	oper = p->OPERATION;
	more = p->MORE_flag;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, p->INVOKE_id);
}

/*
 *  TC_ERROR_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_error_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_error_req *p = (typeof(p)) mp->b_rptr;
	ulong ecode, more;
	struct dg *dg;
	struct iv *iv;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	if (!(iv = iv_lookup(dg, p->INVOKE_id)))
		goto badiid;
	ecode = p->ERROR_code;
	more = p->MORE_flag;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, p->INVOKE_id);
}

/*
 *  TC_CANCEL_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_cancel_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_cancel_req *p = (typeof(p)) mp->b_rptr;
	struct dg *dg;
	struct iv *iv;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	if (!(iv = iv_lookup(dg, p->INVOKE_id)))
		goto badiid;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, p->INVOKE_id);
}

/*
 *  TC_REJECT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tc_reject_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TC_reject_req *p = (typeof(p)) mp->b_rptr;
	ulong pcode;
	struct dg *dg;
	struct iv *iv;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!(dg = dg_lookup(tcap, p->DIALOG_id)))
		goto baddid;
	if (!(iv = iv_lookup(dg, p->INVOKE_id)))
		goto badiid;
	pcode = p->PROBLEM_code;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TCBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tc_error_ack(q, tcap, p->PRIM_type, err, p->DIALOG_id, p->INVOKE_id);
}

/*
 *  TRI Primitives
 *  -----------------------------------------------------------
 */
/*
 *  TR_INFO_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_info_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_info_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, 0);
}

/*
 *  TR_BIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_bind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_bind_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add = NULL;
	ulong xact, flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->ADDR_length) {
		if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
			goto badprim;
		if (p->ADDR_length < sizeof(*add))
			goto badaddr;
		add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
		if (p->ADDR_length != sizeof(*add) + add->alen)
			goto badaddr;
	}
	xact = p->XACT_number;
	flags = p->BIND_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	err = TRBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, 0);
}

/*
 *  TR_UNBIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_unbind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_unbind_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, 0);
}

/*
 *  TR_OPTMGMT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_optmgmt_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_optmgmt_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0, };
	ulong flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	flags = p->MGMT_flags;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, 0);
}

/*
 *  TR_UNI_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_uni_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_uni_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *dst = NULL;
	struct sccp_addr *org = NULL;
	struct tcap_opts opts = { 0, };
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->DEST_length) {
		if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
			goto badprim;
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length != sizeof(*dst) + dst->alen)
			goto badaddr;
	}
	if (p->ORIG_length) {
		if (mp->b_wptr < mp->b_rptr + p->ORIG_offset + p->ORIG_length)
			goto badprim;
		if (p->ORIG_length < sizeof(*org))
			goto badaddr;
		org = (typeof(org)) (mp->b_rptr + p->ORIG_offset);
		if (p->ORIG_length != sizeof(*org) + org->alen)
			goto badaddr;
	}
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TRBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, 0);
}

/*
 *  TR_BEGIN_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_begin_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_begin_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *dst = NULL;
	struct sccp_addr *org = NULL;
	struct tcap_opts opts = { 0, };
	struct tr *tr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->DEST_length) {
		if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
			goto badprim;
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length != sizeof(*dst) + dst->alen)
			goto badaddr;
	}
	if (p->ORIG_length) {
		if (mp->b_wptr < mp->b_rptr + p->ORIG_offset + p->ORIG_length)
			goto badprim;
		if (p->ORIG_length < sizeof(*org))
			goto badaddr;
		org = (typeof(org)) (mp->b_rptr + p->ORIG_offset);
		if (p->ORIG_length != sizeof(*org) + org->alen)
			goto badaddr;
	}
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!p->CORR_id || tr_lookup_cid(tcap, p->CORR_id))
		goto badcid;
	if (!(tr = tcap_alloc_tr(tcap, p->CORR_id, 0)))
		goto enomem;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TRBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, p->CORR_id);
}

/*
 *  TR_BEGIN_RES
 *  -----------------------------------------------------------
 */
STATIC int tr_begin_res(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_begin_res *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *org = NULL;
	struct tcap_opts opts = { 0, };
	struct tr *tr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->ORIG_length) {
		if (mp->b_wptr < mp->b_rptr + p->ORIG_offset + p->ORIG_length)
			goto badprim;
		if (p->ORIG_length < sizeof(*org))
			goto badaddr;
		org = (typeof(org)) (mp->b_rptr + p->ORIG_offset);
		if (p->ORIG_length != sizeof(*org) + org->alen)
			goto badaddr;
	}
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(tr = tr_lookup_tid(tcap, p->TRANS_id)))
		goto badtid;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badtid:
	err = TRBADTID;
	ptrace(("%s: %p: ERROR: bad transaction id\n", TCAP_DRV_NAME, tcap));
	goto error;
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TRBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, p->TRANS_id);
}

/*
 *  TR_CONT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_cont_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_cont_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0, };
	struct tr *tr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(tr = tr_lookup_tid(tcap, p->TRANS_id)))
		goto badtid;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badtid:
	err = TRBADTID;
	ptrace(("%s: %p: ERROR: bad transaction id\n", TCAP_DRV_NAME, tcap));
	goto error;
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, p->TRANS_id);
}

/*
 *  TR_END_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_end_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_end_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0, };
	ulong term;
	struct tr *tr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(tr = tr_lookup_tid(tcap, p->TRANS_id)))
		goto badtid;
	term = p->TERM_scenario;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badtid:
	err = TRBADTID;
	ptrace(("%s: %p: ERROR: bad transaction id\n", TCAP_DRV_NAME, tcap));
	goto error;
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, p->TRANS_id);
}

/*
 *  TR_ABORT_REQ
 *  -----------------------------------------------------------
 */
STATIC int tr_abort_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct TR_abort_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0, };
	ulong cause;
	struct tr *tr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	if (!(tr = tr_lookup_tid(tcap, p->TRANS_id)))
		goto badtid;
	cause = p->ABORT_cause;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badtid:
	err = TRBADTID;
	ptrace(("%s: %p: ERROR: bad transaction id\n", TCAP_DRV_NAME, tcap));
	goto error;
      badopt:
	err = TRBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = TRBADPRIM;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return tr_error_ack(q, tcap, p->PRIM_type, err, p->TRANS_id);
}

/*
 *  TPI Primitives
 *  -----------------------------------------------------------
 */
/*
 *  T_CONN_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_conn_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_conn_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *dst = NULL;
	struct tcap_opts opts = { 0UL, NULL, };
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->DEST_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
		goto badprim;
	if (p->DEST_length < sizeof(*dst))
		goto badprim;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	if (p->DEST_length != sizeof(*dst) + dst->alen)
		goto badaddr;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (!tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      noaddr:
	err = TNOADDR;
	ptrace(("%s: %p: ERROR: could not assign address\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_CONN_RES
 *  -----------------------------------------------------------
 */
STATIC int t_conn_res(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_conn_res *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, NULL, };
	ulong tok, seq;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (!tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	tok = p->ACCEPTOR_id;
	seq = p->SEQ_number;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_DISCON_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_discon_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_discon_req *p = (typeof(p)) mp->b_rptr;
	ulong seq;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	seq = p->SEQ_number;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_DATA_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_data_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_data_req *p = (typeof(p)) mp->b_rptr;
	ulong more;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	more = p->MORE_flag;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_EXDATA_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_exdata_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_exdata_req *p = (typeof(p)) mp->b_rptr;
	ulong more;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	more = p->MORE_flag;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_INFO_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_info_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_info_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_BIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_bind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_bind_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add = NULL;
	ulong cons;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->ADDR_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
		goto badprim;
	if (p->ADDR_length < sizeof(*add))
		goto badaddr;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (p->ADDR_length != sizeof(*add) + add->alen)
		goto badaddr;
	cons = p->CONIND_number;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	err = TBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      noaddr:
	err = TNOADDR;
	ptrace(("%s: %p: ERROR: could not assign address\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_UNBIND_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_unbind_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_unbind_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_UNITDATA_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_unitdata_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_unitdata_req *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *dst;
	struct tcap_opts opts = { 0UL, NULL, };
	if (tcap->ocls != 0)
		goto notsupport;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->DEST_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
		goto badprim;
	if (p->DEST_length < sizeof(*dst))
		goto badaddr;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	if (p->DEST_length != sizeof(*dst) + dst->alen)
		goto badaddr;
	if (p->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (!tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	/* 
	 *  Launch a UNIDIRECTIONAL to the destination address with the
	 *  options as specified.
	 */
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("%s: %p: ERROR: bad address format\n", TCAP_DRV_NAME, tcap));
	goto error;
      noaddr:
	err = TNOADDR;
	ptrace(("%s: %p: ERROR: could not assign address\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: %p: ERROR: primitive not supported for operations class\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_OPTMGMT_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_optmgmt_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_optmgmt_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, NULL, };
	ulong flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (mp->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (!tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	switch ((flags = p->MGMT_flags)) {
	case T_CURRENT:
	case T_NEGOTIATE:
	case T_DEFAULT:
	case T_CHECK:
		break;
	default:
		goto badflag;
	}
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badflag:
	err = TBADFLAG;
	ptrace(("%s: %p: ERROR: bad flags\n", TCAP_DRV_NAME, tcap));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

/*
 *  T_ORDREL_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_ordrel_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_ordrel_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}

#ifdef T_OPTDATA_REQ
/*
 *  T_OPTDATA_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_optdata_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_optdata_req *p = (typeof(p)) mp->b_rptr;
	struct tcap_opts opts = { 0UL, NULL, };
	ulong flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (mp->OPT_length) {
		if (mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
			goto badprim;
		if (!tcap_parse_opts(tcap, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
	}
	flags = p->DATA_flag;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options format\n", TCAP_DRV_NAME, tcap));
	goto error;
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}
#endif

#ifdef T_ADDR_REQ
/*
 *  T_ADDR_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_addr_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_addr_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badprim:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: bad primitive format\n", TCAP_DRV_NAME, tcap));
	goto error;
      error:
	return t_error_ack(q, tcap, p->PRIM_type, err);
}
#endif

#ifdef T_CAPABILITY_REQ
/*
 *  T_CAPABILITY_REQ
 *  -----------------------------------------------------------
 */
STATIC int t_capability_req(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	int err;
	struct T_capability_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	fixme(("Complete this function\n"));
	return (-EFAULT);
}
#endif

/*
 *  -------------------------------------------------------------------------
 *
 *  Primitives received from below (SCCP Provider).
 *
 *  -------------------------------------------------------------------------
 */

/*
 *  NPI (SCCP) Primitives
 *  -----------------------------------------------------------
 */
/*
 *  N_CONN_IND
 *  -----------------------------------------------------------
 */
STATIC int n_conn_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_conn_ind_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *dst;
	N_qos_sel_conn_sccp_t qos = { N_QOS_SEL_CONN_SCCP, sccp->pcls };
	ulong flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->DEST_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
		goto badprim;
	if (p->DEST_length < sizeof(*dst))
		goto badaddr;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	if (p->DEST_length != sizeof(*dst) + dst->alen)
		goto badaddr;
	flags = p->CONN_flags;
	if (p->QOS_length) {
		if (mp->b_wptr < mp->b_rptr + p->QOS_offset + p->QOS_length)
			goto badprim;
		if (p->QOS_length < sizeof(qos))
			goto badqostype;
		bcopy(mp->b_rptr + p->QOS_offset, &qos, sizeof(qos));
		if (qos.n_qos_type != N_QOS_SEL_CONN_SCCP)
			goto badqostype;
		if (qos.protocol_class != 2 && qos.protocol_class != 3)
			goto badqosparam;
	}
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badqosparam:
	pswerr(("%s: %p: SWERR: bad QOS parameter value\n", TCAP_DRV_NAME, sccp));
	goto error;
      badqostype:
	pswerr(("%s: %p: SWERR: bad QOS structure type\n", TCAP_DRV_NAME, sccp));
	goto error;
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_CONN_CON
 *  -----------------------------------------------------------
 */
STATIC int n_conn_con(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_conn_con_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *res = &sccp->rem;
	N_qos_sel_conn_sccp_t qos = { N_QOS_SEL_CONN_SCCP, sccp->pcls };
	ulong flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->RES_length) {
		if (mp->b_wptr < mp->b_rptr + p->RES_offset + p->RES_length)
			goto badprim;
		if (p->RES_length < sizeof(*res))
			goto badaddr;
		res = (typeof(res)) (mp->b_rptr + p->RES_offset);
		if (p->RES_length < sizeof(*res) + res->alen)
			goto badaddr;
	}
	flags = p->CONN_flags;
	if (p->QOS_length) {
		if (mp->b_wptr < mp->b_rptr + p->QOS_offset + p->QOS_length)
			goto badprim;
		if (p->QOS_length < sizeof(qos))
			goto badqostype;
		bcopy(mp->b_rptr + p->QOS_offset, &qos, sizeof(qos));
		if (qos.n_qos_type != N_QOS_SEL_CONN_SCCP)
			goto badqostype;
		if (qos.protocol_class != 2 && qos.protocol_class != 3)
			goto badqosparam;
	}
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badqosparam:
	pswerr(("%s: %p: SWERR: bad QOS parameter value\n", TCAP_DRV_NAME, sccp));
	goto error;
      badqostype:
	pswerr(("%s: %p: SWERR: bad QOS structure type\n", TCAP_DRV_NAME, sccp));
	goto error;
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_DISCON_IND
 *  -----------------------------------------------------------
 */
STATIC int n_discon_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_discon_ind_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *res = &sccp->rem;
	ulong orig, reason, seq;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->RES_length) {
		if (mp->b_wptr < mp->b_rptr + p->RES_offset + p->RES_length)
			goto badprim;
		if (p->RES_length < sizeof(*res))
			goto badaddr;
		res = (typeof(res)) (mp->b_rptr + p->RES_offset);
		if (p->RES_length < sizeof(*res) + res->alen)
			goto badaddr;
	}
	orig = p->DISCON_orig;
	reason = p->DISCON_reason;
	seq = p->SEQ_number;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_DATA_IND
 *  -----------------------------------------------------------
 */
STATIC int n_data_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_data_ind_t *p = (typeof(p)) mp->b_rptr;
	ulong flags;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	flags = p->DATA_xfer_flags;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_EXDATA_IND
 *  -----------------------------------------------------------
 */
STATIC int n_exdata_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_exdata_ind_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_INFO_ACK
 *  -----------------------------------------------------------
 */
STATIC int n_info_ack(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_info_ack_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add = NULL;
	N_qos_sel_info_sccp_t qos;
	N_qos_range_info_sccp_t qor;
	uchar *pro = NULL;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (p->ADDR_length) {
		if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
			goto badprim;
		if (p->ADDR_length < sizeof(*add))
			goto badaddr;
		add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
		if (p->ADDR_length < sizeof(*add) + add->alen)
			goto badaddr;
	}
	if (p->QOS_length) {
		if (mp->b_wptr < mp->b_rptr + p->QOS_offset + p->QOS_length)
			goto badprim;
		if (p->QOS_length < sizeof(qos))
			goto badqostype;
		bcopy(mp->b_rptr + p->QOS_offset, &qos, sizeof(qos));
		if (qos.n_qos_type != N_QOS_SEL_INFO_SCCP)
			goto badqostype;
		if (qos.protocol_class < 0 || qos.protocol_class > 3)
			goto badqosparam;
	}
	if (p->QOS_range_length) {
		if (mp->b_wptr < mp->b_rptr + p->QOS_range_offset + p->QOS_range_length)
			goto badprim;
		if (p->QOS_range_length < sizeof(qor))
			goto badqostype;
		bcopy(mp->b_rptr + p->QOS_range_offset, &qor, sizeof(qor));
		if (qor.n_qos_type != N_QOS_RANGE_INFO_SCCP)
			goto badqostype;
	}
	if (p->PROTOID_length) {
		if (mp->b_wptr < mp->b_rptr + p->PROTOID_offset + p->PROTOID_length)
			goto badprim;
		pro = (typeof(pro)) (mp->b_rptr + p->PROTOID_offset);
	}
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badqosparam:
	pswerr(("%s: %p: SWERR: bad QOS parameter value\n", TCAP_DRV_NAME, sccp));
	goto error;
      badqostype:
	pswerr(("%s: %p: SWERR: bad QOS structure type\n", TCAP_DRV_NAME, sccp));
	goto error;
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_BIND_ACK
 *  -----------------------------------------------------------
 */
STATIC int n_bind_ack(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_bind_ack_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add;
	uchar *pro = NULL;
	ulong conind, tok;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->ADDR_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
		goto badprim;
	if (p->ADDR_length < sizeof(*add))
		goto badaddr;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (p->ADDR_length < sizeof(*add) + add->alen)
		goto badaddr;
	conind = p->CONIND_number;
	tok = p->TOKEN_value;
	if (p->PROTOID_length) {
		if (mp->b_wptr < mp->b_rptr + p->PROTOID_offset + p->PROTOID_length)
			goto badprim;
		pro = (typeof(pro)) (mp->b_rptr + p->PROTOID_offset);
	}
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_ERROR_ACK
 *  -----------------------------------------------------------
 */
STATIC int n_error_ack(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_error_ack_t *p = (typeof(p)) mp->b_rptr;
	ulong prim, etype;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	prim = p->ERROR_prim;
	etype = (p->NPI_error == NSYSERR) ? -p->UNIX_error : p->NPI_error;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_OK_ACK
 *  -----------------------------------------------------------
 */
STATIC int n_ok_ack(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_ok_ack_t *p = (typeof(p)) mp->b_rptr;
	ulong prim;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	prim = p->CORRECT_prim;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_UNITDATA_IND
 *  -----------------------------------------------------------
 */
STATIC int n_unitdata_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_unitdata_ind_t *p = (typeof(p)) mp->b_rptr;
	struct tr_msg msg;
	struct tcap_opts opts;
	struct sccp_addr *src;
	struct sccp_addr *dst = &sccp->loc;
	mblk_t *dp;
	size_t dlen;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->SRC_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->SRC_offset + p->SRC_length)
		goto badprim;
	if (p->SRC_length < sizeof(*src))
		goto badaddr;
	src = (typeof(src)) (mp->b_rptr + p->SRC_offset);
	if (p->SRC_length != sizeof(*src) + src->alen)
		goto badaddr;
	bcopy(src, &msg.sccp.orig, sizeof(*src) + src->alen);
	if (p->DEST_length) {
		if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
			goto badprim;
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length != sizeof(*dst) + dst->alen)
			goto badaddr;
		if (dst->ni != sccp->loc.ni || dst->pc != sccp->loc.pc || dst->ssn != sccp->loc.ssn)
			goto badaddr;
	}
	bcopy(dst, &msg.sccp.dest, sizeof(*dst) + dst->alen);
	if (p->QOS_length) {
		N_qos_sel_data_sccp_t qos = { N_QOS_SEL_DATA_SCCP, };
		if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
			goto badprim;
		if (p->QOS_length < sizeof(qos))
			goto badqostype;
		bcopy(mp->b_rptr + QOS_offset, &qos, sizeof(qos));
		if (qos.n_qos_type != N_QOS_SEL_DATA_SCCP)
			goto badqostype;
		msg.sccp.pcl = qos.protocol_class;
		msg.sccp.ret = qos.option_flags;
		msg.sccp.imp = qos.importance;
		msg.sccp.seq = qos.sequence_control;
		msg.sccp.sls = qos.sequence_control;
		msg.sccp.mp = qos.message_priority;
	} else {
		msg.sccp.pcl = sccp->options.pcl;
		msg.sccp.ret = sccp->options.ret;
		msg.sccp.imp = sccp->options.imp;
		msg.sccp.seq = sccp->options.seq;
		msg.sccp.sls = sccp->options.sls;
		msg.sccp.mp = sccp->options.mp;
	}
	opts.pcl = &msg.sccp.pcl;
	opts.ret = &msg.sccp.ret;
	opts.imp = &msg.sccp.imp;
	opts.seq = &msg.sccp.seq;
	opts.sls = &msg.sccp.sls;
	opts.mp = &msg.sccp.mp;
	opts.flags = (TF_OPT_PCL | TF_OPT_RET | TF_OPT_IMP | TF_OPT_SEQ | TF_OPT_SLS | TF_OPT_MP);
	if (!(dp = mp->b_cont) || !(dlen = msgdsize(dp)) || dlen > sccp->prot.NSDU_size)
		goto baddata;
	if ((err = ss7_pullupmsg(dp, -1)) < 0)
		goto error;
	if ((err = tcap_dec_msg(dp->b_rtpr, dp->b_wptr, &msg)) < 0)
		goto error;
	if ((err = tcap_recv_msg(q, tcap, &opts, &msg)) < 0)
		goto error;
	return (err);
      baddata:
	pswerr(("%s: %p: SWERR: bad data\n", TCAP_DRV_NAME, sccp));
	goto error;
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_UDERROR_IND
 *  -----------------------------------------------------------
 */
STATIC int n_uderror_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_uderror_ind_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *dst;
	ulong etype;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->DEST_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length)
		goto badprim;
	if (p->DEST_length < sizeof(*dst))
		goto badaddr;
	dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
	if (p->DEST_length < sizeof(*dst) + dst->alen)
		goto badaddr;
	etype = p->ERROR_type;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_DATACK_IND
 *  -----------------------------------------------------------
 */
STATIC int n_datack_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_datack_ind_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_RESET_IND
 *  -----------------------------------------------------------
 */
STATIC int n_reset_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_reset_ind_t *p = (typeof(p)) mp->b_rptr;
	ulong orig, reason;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	orig = p->RESET_orig;
	reason = p->RESET_reason;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_RESET_CON
 *  -----------------------------------------------------------
 */
STATIC int n_reset_con(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_reset_con_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	goto notsupport;
      notsupport:
	pswerr(("%s: %p: SWERR: unsupported primitive from below\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_COORD_IND
 *  -----------------------------------------------------------
 */
STATIC int n_coord_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_coord_ind_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add;
	ulong smi;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->ADDR_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
		goto badprim;
	if (p->ADDR_length < sizeof(*add))
		goto badaddr;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (p->ADDR_length != sizeof(*add) + add->alen)
		goto badaddr;
	smi = p->SMI;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_COORD_CON
 *  -----------------------------------------------------------
 */
STATIC int n_coord_con(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_coord_con_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add;
	ulong smi;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->ADDR_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
		goto badprim;
	if (p->ADDR_length < sizeof(*add))
		goto badaddr;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (p->ADDR_length != sizeof(*add) + add->alen)
		goto badaddr;
	smi = p->SMI;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_STATE_IND
 *  -----------------------------------------------------------
 */
STATIC int n_state_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_state_ind_t *p = (typeof(p)) mp->b_rptr;
	struct sccp_addr *add;
	ulong status, smi;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->ADDR_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
		goto badprim;
	if (p->ADDR_length < sizeof(*add))
		goto badaddr;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (p->ADDR_length != sizeof(*add) + add->alen)
		goto badaddr;
	status = p->STATUS;
	smi = p->SMI;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  N_PCSTATE_IND
 *  -----------------------------------------------------------
 */
STATIC int n_pcstate_ind(queue_t *q, struct sccp *sccp, mblk_t *mp)
{
	N_pcstate_ind_t *p = (typeof(p)) mp->b_rptr;
	struct mtp_addr *add;
	ulong status;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto badprim;
	if (!p->ADDR_length)
		goto noaddr;
	if (mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_length)
		goto badprim;
	if (p->ADDR_length < sizeof(*add))
		goto badaddr;
	add = (typeof(add)) (mp->b_rptr + p->ADDR_offset);
	if (p->ADDR_length != sizeof(*add) + add->alen)
		goto badaddr;
	status = p->STATUS;
	fixme(("Complete this function\n"));
	return (-EFAULT);
      badaddr:
	pswerr(("%s: %p: SWERR: bad address\n", TCAP_DRV_NAME, sccp));
	goto error;
      noaddr:
	pswerr(("%s: %p: SWERR: could not assign address\n", TCAP_DRV_NAME, sccp));
	goto error;
      badprim:
	pswerr(("%s: %p: SWERR: invalid primitive format\n", TCAP_DRV_NAME, sccp));
	goto error;
      error:
	return sccp_hangup(q, sccp, -EPROTO);
}

/*
 *  =========================================================================
 *
 *  I/O Controls
 *
 *  =========================================================================
 */

/*
 *  TCAP_IOCGOPTIONS
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocgoptions(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCSOPTIONS
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocsoptions(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCGCONFIG
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocgconfig(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCSCONFIG
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocsconfig(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCTCONFIG
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioctconfig(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCCCONFIG
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioccconfig(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCGSTATEM
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocgstatem(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCCMRESET
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioccmreset(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCGSTATSP
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocgstatsp(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCSSTATSP
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocsstatsp(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCGSTATS
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocgstats(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCCSTATS
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioccstats(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCGNOTIFY
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocgnotify(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCSNOTIFY
 *  -----------------------------------------------------------
 */
STATIC int tcap_iocsnotify(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCCNOTIFY
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioccnotify(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCCMGMT
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioccmgmt(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  TCAP_IOCCPASS
 *  -----------------------------------------------------------
 */
STATIC int tcap_ioccpass(queue_t *q, struct tcap *tcap, mblk_t *mp)
{
	swerr();
	return (-EFAULT);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int tcap_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct tcap *tcap = TCAP_PRIV(q);
	struct iocblk *iocp = (typeof(iocp)) mp->b_rptr;
	void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL;
	int cmd = iocp->ioc_cmd, count = iocp->ioc_count;
	int type = _IOC_TYPE(cmd), nr = _IOC_NR(cmd), size = _IOC_SIZE(cmd);
	int ret = 0;
	switch (type) {
	case _IOC_TYPE(__SID):
	{
		int flags;
		struct sccp *sccp, **sccpp;
		struct linkblk *lb;
		if (!(lb = arg)) {
			swerr();
			ret = -EINVAL;
			break;
		}
		switch (nr) {
		case _IOC_NR(I_PLINK):
			ptrace(("%s: %p: I_PLINK\n", TCAP_DRV_NAME, tcap));
			if (iocp->ioc_cr->cr_uid != 0) {
				ptrace(("%s: %p: ERROR: Non-root attempt to I_PLINK\n", TCAP_DRV_NAME, tcap));
				ret = -EPERM;
				break;
			}
		case _IOC_NR(I_LINK):
			ptrace(("%s: %p: I_LINK\n", TCAP_DRV_NAME, tcap));
			MOD_INC_USE_COUNT;	/* keep module from unloading */
			lis_spin_lock_irqsave(&master.lock, &flags);
			{
				/* place in list in ascending index order */
				for (sccpp = &master.sccp.list; *sccpp && (*sccpp)->u.mux.index < lb->l_index;
				     sccpp = &(*sccpp)->next) ;
				if ((sccp = tcap_alloc_sccp(lb->l_qbot, sccpp, lb->l_index, iocp->ioc_cr))) {
					lis_spin_unlock_irqrestore(&master.lock, &flags);
					break;
				}
				MOD_DEC_USE_COUNT;
				ret = -ENOMEM;
			}
			lis_spin_unlock_irqrestore(&master.lock, &flags);
			break;
		case _IOC_NR(I_PUNLINK):
			ptrace(("%s: %p: I_PUNLINK\n", TCAP_DRV_NAME, tcap));
			if (iocp->ioc_cr->cr_uid != 0) {
				ptrace(("%s: %p: ERROR: Non-root attempt to I_PUNLINK\n", TCAP_DRV_NAME, tcap));
				ret = -EPERM;
				break;
			}
		case _IOC_NR(I_UNLINK):
			ptrace(("%s: %p: I_UNLINK\n", TCAP_DRV_NAME, tcap));
			lis_spin_lock_irqsave(&master.lock, &flags);
			{
				for (sccp = master.sccp.list; sccp; sccp = sccp->next)
					if (sccp->u.mux.index == lb->l_index)
						break;
				if (!sccp) {
					ret = -EINVAL;
					ptrace(("%s: %p: ERROR: Couldn't find I_UNLINK muxid\n", TCAP_DRV_NAME,
						tcap));
					lis_spin_unlock_irqrestore(&master.lock, &flags);
					break;
				}
				tcap_free_sccp(sccp->iq);
				MOD_DEC_USE_COUNT;
			}
			lis_spin_unlock_irqrestore(&master.lock, &flags);
			break;
		default:
		case _IOC_NR(I_STR):
			ptrace(("%s: %p: ERROR: Unsupported STREAMS ioctl %c, %d\n", TCAP_DRV_NAME, tcap,
				(char) type, nr));
			ret = -EOPNOTSUPP;
			break;
		}
		break;
	}
	case TCAP_IOC_MAGIC:
	{
		if (count < size) {
			ret = -EINVAL;
			break;
		}
		switch (nr) {
		case _IOC_NR(TCAP_IOCGOPTIONS):
			printd(("%s; %p: -> TCAP_IOCGOPTIONS\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocgoptions(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCSOPTIONS):
			printd(("%s; %p: -> TCAP_IOCSOPTIONS\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocsoptions(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCGCONFIG):
			printd(("%s; %p: -> TCAP_IOCGCONFIG\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocgconfig(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCSCONFIG):
			printd(("%s; %p: -> TCAP_IOCSCONFIG\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocsconfig(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCTCONFIG):
			printd(("%s; %p: -> TCAP_IOCTCONFIG\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioctconfig(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCCCONFIG):
			printd(("%s; %p: -> TCAP_IOCCCONFIG\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioccconfig(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCGSTATEM):
			printd(("%s; %p: -> TCAP_IOCGSTATEM\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocgstatem(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCCMRESET):
			printd(("%s; %p: -> TCAP_IOCCMRESET\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioccmreset(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCGSTATSP):
			printd(("%s; %p: -> TCAP_IOCGSTATSP\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocgstatsp(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCSSTATSP):
			printd(("%s; %p: -> TCAP_IOCSSTATSP\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocsstatsp(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCGSTATS):
			printd(("%s; %p: -> TCAP_IOCGSTATS\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocgstats(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCCSTATS):
			printd(("%s; %p: -> TCAP_IOCCSTATS\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioccstats(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCGNOTIFY):
			printd(("%s; %p: -> TCAP_IOCGNOTIFY\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocgnotify(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCSNOTIFY):
			printd(("%s; %p: -> TCAP_IOCSNOTIFY\n", TCAP_DRV_NAME, tcap));
			ret = tcap_iocsnotify(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCCNOTIFY):
			printd(("%s; %p: -> TCAP_IOCCNOTIFY\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioccnotify(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCCMGMT):
			printd(("%s; %p: -> TCAP_IOCCMGMT\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioccmgmt(q, tcap, mp);
			break;
		case _IOC_NR(TCAP_IOCCPASS):
			printd(("%s; %p: -> TCAP_IOCCPASS\n", TCAP_DRV_NAME, tcap));
			ret = tcap_ioccpass(q, tcap, mp);
			break;
		default:
			ptrace(("%s: %p: ERROR: Unsupported TCAP ioctl %c, %d\n", TCAP_DRV_NAME, tcap,
				(char) type, nr));
			ret = -EOPNOTSUPP;
			break;
		}
		break;
	}
	default:
		ptrace(("%s: %p: ERROR: Unsupported ioctl %c, %d\n", TCAP_DRV_NAME, tcap, (char) type, nr));
		ret = -EOPNOTSUPP;
		break;
	}
	if (ret > 0) {
		return (ret);
	} else if (ret == 0) {
		mp->b_datap->db_type = M_IOCACK;
		iocp->ioc_error = 0;
		iocp->ioc_rval = 0;
	} else {
		mp->b_datap->db_type = M_IOCNAK;
		iocp->ioc_error = -ret;
		iocp->ioc_rval = -1;
	}
	qreply(q, mp);
	return (QR_ABSORBED);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sccp_r_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	struct sccp *sccp = SCCP_PRIV(q);
	ulong oldstate = sccp_get_i_state(sccp);
	switch (*(ulong *) mp->b_rptr) {
	case N_CONN_IND:
		printd(("%s: %p: N_CONN_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_conn_ind(q, sccp, mp);
		break;
	case N_CONN_CON:
		printd(("%s: %p: N_CONN_CON <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_conn_con(q, sccp, mp);
		break;
	case N_DISCON_IND:
		printd(("%s: %p: N_DISCON_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_discon_ind(q, sccp, mp);
		break;
	case N_DATA_IND:
		printd(("%s: %p: N_DATA_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_data_ind(q, sccp, mp);
		break;
	case N_EXDATA_IND:
		printd(("%s: %p: N_EXDATA_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_exdata_ind(q, sccp, mp);
		break;
	case N_INFO_ACK:
		printd(("%s: %p: N_INFO_ACK <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_info_ack(q, sccp, mp);
		break;
	case N_BIND_ACK:
		printd(("%s: %p: N_BIND_ACK <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_bind_ack(q, sccp, mp);
		break;
	case N_ERROR_ACK:
		printd(("%s: %p: N_ERROR_ACK <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_error_ack(q, sccp, mp);
		break;
	case N_OK_ACK:
		printd(("%s: %p: N_OK_ACK <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_ok_ack(q, sccp, mp);
		break;
	case N_UNITDATA_IND:
		printd(("%s: %p: N_UNITDATA_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_unitdata_ind(q, sccp, mp);
		break;
	case N_UDERROR_IND:
		printd(("%s: %p: N_UDERROR_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_uderror_ind(q, sccp, mp);
		break;
	case N_DATACK_IND:
		printd(("%s: %p: N_DATACK_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_datack_ind(q, sccp, mp);
		break;
	case N_RESET_IND:
		printd(("%s: %p: N_RESET_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_reset_ind(q, sccp, mp);
		break;
	case N_RESET_CON:
		printd(("%s: %p: N_RESET_CON <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_reset_con(q, sccp, mp);
		break;
	case N_COORD_IND:
		printd(("%s: %p: N_COORD_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_coord_ind(q, sccp, mp);
		break;
	case N_COORD_CON:
		printd(("%s: %p: N_COORD_CON <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_coord_con(q, sccp, mp);
		break;
	case N_STATE_IND:
		printd(("%s: %p: N_STATE_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_state_ind(q, sccp, mp);
		break;
	case N_PCSTATE_IND:
		printd(("%s: %p: N_PCSTATE_IND <-\n", TCAP_DRV_NAME, sccp));
		rtn = n_pcstate_ind(q, sccp, mp);
		break;
	default:
		printd(("%s: %p: ???? <-\n", TCAP_DRV_NAME, sccp));
		rtn = -EFAULT;
		break;
	}
	if (rtn < 0)
		sccp_set_i_state(sccp, oldstate);
	return (rtn);
}
STATIC int tcap_w_proto(queue_t *q, mblk_t *mp)
{
	int rtn;
	struct tcap *tcap = TCAP_PRIV(q);
	switch (tcap->i_style) {
	case TCAP_STYLE_TCI:
	{
		ulong oldstate = tcap_get_c_state(tcap);
		switch (*(ulong *) mp->b_rptr) {
		case TC_INFO_REQ:
			printd(("%s: %p: -> TC_INFO_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_info_req(q, tcap, mp);
			break;
		case TC_BIND_REQ:
			printd(("%s: %p: -> TC_BIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_bind_req(q, tcap, mp);
			break;
		case TC_UNBIND_REQ:
			printd(("%s: %p: -> TC_UNBIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_unbind_req(q, tcap, mp);
			break;
		case TC_SUBS_BIND_REQ:
			printd(("%s: %p: -> TC_SUBS_BIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_subs_bind_req(q, tcap, mp);
			break;
		case TC_SUBS_UNBIND_REQ:
			printd(("%s: %p: -> TC_SUBS_UNBIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_subs_unbind_req(q, tcap, mp);
			break;
		case TC_OPTMGMT_REQ:
			printd(("%s: %p: -> TC_OPTMGMT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_optmgmt_req(q, tcap, mp);
			break;
		case TC_UNI_REQ:
			printd(("%s: %p: -> TC_UNI_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_uni_req(q, tcap, mp);
			break;
		case TC_BEGIN_REQ:
			printd(("%s: %p: -> TC_BEGIN_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_begin_req(q, tcap, mp);
			break;
		case TC_BEGIN_RES:
			printd(("%s: %p: -> TC_BEGIN_RES\n", TCAP_DRV_NAME, tcap));
			rtn = tc_begin_res(q, tcap, mp);
			break;
		case TC_CONT_REQ:
			printd(("%s: %p: -> TC_CONT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_cont_req(q, tcap, mp);
			break;
		case TC_END_REQ:
			printd(("%s: %p: -> TC_END_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_end_req(q, tcap, mp);
			break;
		case TC_ABORT_REQ:
			printd(("%s: %p: -> TC_ABORT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_abort_req(q, tcap, mp);
			break;
		case TC_INVOKE_REQ:
			printd(("%s: %p: -> TC_INVOKE_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_invoke_req(q, tcap, mp);
			break;
		case TC_RESULT_REQ:
			printd(("%s: %p: -> TC_RESULT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_result_req(q, tcap, mp);
			break;
		case TC_ERROR_REQ:
			printd(("%s: %p: -> TC_ERROR_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_error_req(q, tcap, mp);
			break;
		case TC_CANCEL_REQ:
			printd(("%s: %p: -> TC_CANCEL_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_cancel_req(q, tcap, mp);
			break;
		case TC_REJECT_REQ:
			printd(("%s: %p: -> TC_REJECT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tc_reject_req(q, tcap, mp);
			break;
		default:
			printd(("%s: %p: -> ????\n", TCAP_DRV_NAME, tcap));
			rtn = -EPROTO;
			break;
		}
		if (rtn < 0)
			tcap_set_c_state(tcap, oldstate);
		break;
	}
	case TCAP_STYLE_TRI:
	{
		ulong oldstate = tcap_get_r_state(tcap);
		switch (*(ulong *) mp->b_rptr) {
		case TR_INFO_REQ:
			printd(("%s: %p: -> TR_INFO_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_info_req(q, tcap, mp);
			break;
		case TR_BIND_REQ:
			printd(("%s: %p: -> TR_BIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_bind_req(q, tcap, mp);
			break;
		case TR_UNBIND_REQ:
			printd(("%s: %p: -> TR_UNBIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_unbind_req(q, tcap, mp);
			break;
		case TR_OPTMGMT_REQ:
			printd(("%s: %p: -> TR_OPTMGMT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_optmgmt_req(q, tcap, mp);
			break;
		case TR_UNI_REQ:
			printd(("%s: %p: -> TR_UNI_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_uni_req(q, tcap, mp);
			break;
		case TR_BEGIN_REQ:
			printd(("%s: %p: -> TR_BEGIN_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_begin_req(q, tcap, mp);
			break;
		case TR_BEGIN_RES:
			printd(("%s: %p: -> TR_BEGIN_RES\n", TCAP_DRV_NAME, tcap));
			rtn = tr_begin_res(q, tcap, mp);
			break;
		case TR_CONT_REQ:
			printd(("%s: %p: -> TR_CONT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_cont_req(q, tcap, mp);
			break;
		case TR_END_REQ:
			printd(("%s: %p: -> TR_END_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_end_req(q, tcap, mp);
			break;
		case TR_ABORT_REQ:
			printd(("%s: %p: -> TR_ABORT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = tr_abort_req(q, tcap, mp);
			break;
		default:
			printd(("%s: %p: -> ????\n", TCAP_DRV_NAME, tcap));
			rtn = -EPROTO;
			break;
		}
		if (rtn < 0)
			tcap_set_r_state(tcap, oldstate);
		break;
	}
	case TCAP_STYLE_TPI:
	{
		ulong oldstate = tcap_get_t_state(tcap);
		switch (*(ulong *) mp->b_rptr) {
		case T_CONN_REQ:
			printd(("%s: %p: -> T_CONN_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_conn_req(q, tcap, mp);
			break;
		case T_CONN_RES:
			printd(("%s: %p: -> T_CONN_RES\n", TCAP_DRV_NAME, tcap));
			rtn = t_conn_res(q, tcap, mp);
			break;
		case T_DISCON_REQ:
			printd(("%s: %p: -> T_DISCON_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_discon_req(q, tcap, mp);
			break;
		case T_DATA_REQ:
			printd(("%s: %p: -> T_DATA_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_data_req(q, tcap, mp);
			break;
		case T_EXDATA_REQ:
			printd(("%s: %p: -> T_EXDATA_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_exdata_req(q, tcap, mp);
			break;
		case T_INFO_REQ:
			printd(("%s: %p: -> T_INFO_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_info_req(q, tcap, mp);
			break;
		case T_BIND_REQ:
			printd(("%s: %p: -> T_BIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_bind_req(q, tcap, mp);
			break;
		case T_UNBIND_REQ:
			printd(("%s: %p: -> T_UNBIND_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_unbind_req(q, tcap, mp);
			break;
		case T_UNITDATA_REQ:
			printd(("%s: %p: -> T_UNITDATA_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_unitdata_req(q, tcap, mp);
			break;
		case T_OPTMGMT_REQ:
			printd(("%s: %p: -> T_OPTMGMT_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_optmgmt_req(q, tcap, mp);
			break;
		case T_ORDREL_REQ:
			printd(("%s: %p: -> T_ORDREL_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_ordrel_req(q, tcap, mp);
			break;
		case T_OPTDATA_REQ:
			printd(("%s: %p: -> T_OPTDATA_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_optdata_req(q, tcap, mp);
			break;
		case T_ADDR_REQ:
			printd(("%s: %p: -> T_ADDR_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_addr_req(q, tcap, mp);
			break;
		case T_CAPABILITY_REQ:
			printd(("%s: %p: -> T_CAPABILITY_REQ\n", TCAP_DRV_NAME, tcap));
			rtn = t_capability_req(q, tcap, mp);
			break;
		default:
			printd(("%s: %p: -> ???\n", TCAP_DRV_NAME, tcap));
			rtn = -EPROTO;
			break;
		}
		if (rtn < 0)
			tcap_set_t_state(tcap, oldstate);
		break;
	}
	case TCAP_STYLE_MGMT:
	{
		ulong oldstate = tcap_get_m_state(tcap);
		switch (*(ulong *) mp->b_rptr) {
		}
		if (rtn < 0)
			tcap_set_m_state(tcap, oldstate);
		break;
	}
	default:
		swerr();
		rtn = -EPROTO;
		break;
	}
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC int sccp_r_data(queue_t *q, mblk_t *mp)
{
	struct sccp *sccp = SCCP_PRIV(q);
	return n_data(q, sccp, mp);
}
STATIC int tcap_w_data(queue_t *q, mblk_t *mp)
{
	struct tcap *tcap = TCAP_PRIV(q);
	switch (tcap->i_style) {
	case TCAP_STYLE_TRI:
		return tr_data(q, tcap, mp);
	case TCAP_STYLE_TCI:
		return tc_data(q, tcap, mp);
	case TCAP_STYLE_TPI:
		return t_data(q, tcap, mp);
	case TCAP_STYLE_MGMT:
		return mgm_data(q, tcap, mp);
	default:
		swerr();
		return (-EFAULT);
	}
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR, M_HANGUP Handling
 *
 *  -------------------------------------------------------------------------
 */
STATIC INLINE int sccp_r_error(queue_t *q, mblk_t *mp)
{
	struct sccp *sccp = SCCP_PRIV(q);
	return sccp_hangup(q, sccp);
}
STATIC INLINE int sccp_r_hangup(queue_t *q, mblk_t *mp)
{
	struct sccp *sccp = SCCP_PRIV(q);
	return sccp_hangup(q, sccp);
}

/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
STATIC INLINE int tcap_r_prim(queue_t *q, mblk_t *mp)
{
	switch (mp->b_datap->db_type) {
	case M_FLUSH:
		return ss7_r_flush(q, mp);
	}
	return (QR_PASSFLOW);
}
STATIC INLINE int tcap_w_prim(queue_t *q, mblk_t *mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return tcap_w_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return tcap_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return tcap_w_proto(q, mp);
	case M_FLUSH:
		return ss7_w_flush(q, mp);
	case M_IOCTL:
		return tcap_w_ioctl(q, mp);
	}
	seldom();
	return (QR_PASSFLOW);
}
STATIC INLINE int sccp_r_prim(queue_t *q, mblk_t *mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return sccp_r_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sccp_r_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sccp_r_proto(q, mp);
	case M_FLUSH:
		return ss7_r_flush(q, mp);
	case M_ERROR:
		return sccp_r_error(q, mp);
	case M_HANGUP:
		return sccp_r_hangup(q, mp);
	}
	seldom();
	return (QR_PASSFLOW);
}
STATIC INLINE int sccp_w_prim(queue_t *q, mblk_t *mp)
{
	switch (mp->b_datap->db_type) {
	case M_FLUSH:
		return ss7_w_flush(q, mp);
	}
	return (QR_PASSFLOW);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  -------------------------------------------------------------------------
 */
STATIC int tcap_majors[TCAP_NMAJOR] = { TCAP_CMAJOR, };
STATIC int tcap_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
	int flags, mindex = 0;
	ushort cmajor = getmajor(*devp);
	ushort cminor = getminor(*devp);
	ushort bminor = cminor;
	struct tcap *tcap, **tcapp = &master.tcap.list;
	MOD_INC_USE_COUNT;	/* keep module from unloading */
	if (q->q_ptr != NULL) {
		MOD_DEC_USE_COUNT;
		return (0);	/* already open */
	}
	if (sflag == MODOPEN || WR(q)->q_next) {
		ptrace(("%s: ERROR: cannot push as module\n", TCAP_DRV_NAME));
		MOD_DEC_USE_COUNT;
		return (EIO);
	}
	if (cmajor != TCAP_CMAJOR || cminor >= TCAP_CMINOR_FREE) {
		MOD_DEC_USE_COUNT;
		return (ENXIO);
	}
	/* allocate a new device */
	cminor = TCAP_CMINOR_FREE;
	lis_spin_lock_irqsave(&master.lock, &flags);
	for (; *tcapp; tcapp = &(*tcapp)->next) {
		ushort dmajor = (*tcapp)->u.dev.cmajor;
		if (cmajor != dmajor)
			break;
		if (cmajor == dmajor) {
			ushort dminor = (*tcapp)->u.dev.cminor;
			if (cminor < dminor)
				break;
			if (cminor > dminor)
				continue;
			if (cminor == dminor) {
				if (++cminor >= TCAP_NMINOR) {
					if (++mindex >= TCAP_NMAJOR || !(cmajor = tcap_majors[mindex]))
						break;
					cminor = 0;
				}
				continue;
			}
		}
	}
	if (mindex >= TCAP_NMAJOR || !cmajor) {
		ptrace(("%s: ERROR: no device numbers available\n", TCAP_DRV_NAME));
		lis_spin_unlock_irqrestore(&master.lock, &flags);
		MOD_DEC_USE_COUNT;
		return (ENXIO);
	}
	printd(("%s: opened character device %d:%d\n", TCAP_DRV_NAME, cmajor, cminor));
	*devp = makedevice(cmajor, cminor);
	if (!(tcap = tcap_alloc_priv(q, tcapp, devp, crp, bminor))) {
		ptrace(("%s: ERROR: no memory\n", TCAP_DRV_NAME));
		lis_spin_unlock_irqrestore(&master.lock, &flags);
		MOD_DEC_USE_COUNT;
		return (ENOMEM);
	}
	lis_spin_unlock_irqrestore(&master.lock, &flags);
	return (0);
}

/*
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
STATIC int tcap_close(queue_t *q, int flag, cred_t *crp)
{
	struct tcap *tcap = TCAP_PRIV(q);
	int flags;
	(void) flag;
	(void) crp;
	(void) tcap;
	printd(("%s: closing character device %d:%d\n", TCAP_DRV_NAME, tcap->u.dev.cmajor, tcap->u.dev.cminor));
	lis_spin_lock_irqsave(&master.lock, &flags);
	tcap_free_priv(tcap);
	lis_spin_unlock_irqrestore(&master.lock, &flags);
	MOD_DEC_USE_COUNT;
	return (0);
}

/*
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
STATIC int tcap_initialized = 0;
STATIC void tcap_init(void)
{
	int rtn, major;
	unless(tcap_initialized, return);
	cmn_err(CE_NOTE, TCAP_BANNER);	/* console splash */
	if ((rtn = tcap_init_caches())) {
		cmn_err(CE_PANIC, "%s: ERROR: Counld not allocated caches", TCAP_DRV_NAME);
		tcap_initialized = rtn;
		return;
	}
	for (major = 0; major < TCAP_NMAJOR; major++) {
		if ((rtn = lis_register_strdev(tcap_majors[major], &tcap_info, TCAP_NMINOR, TCAP_DRV_NAME)) <= 0) {
			if (!major) {
				cmn_err(CE_PANIC, "%s: Can't register 1'st major %d", TCAP_DRV_NAME,
					tcap_majors[0]);
				tcap_term_caches();
				tcap_initialized = rtn;
				return;
			}
			cmn_err(CE_WARN, "%s: Can't register %d'th major", TCAP_DRV_NAME, major + 1);
			tcap_majors[major] = 0;
		} else if (major)
			tcap_majors[major] = rtn;
	}
	lis_spin_lock_init(&master.lock, "tcap-open-list-lock");
	tcap_initialized = 1;
	return;
}
STATIC void tcap_terminate(void)
{
	int rtn, major;
	for (major = 0; major < TCAP_NMAJOR; major++) {
		if (tcap_majors[major]) {
			if ((rtn = lis_unregister_strdev(tcap_majors[major])))
				cmn_err(CE_PANIC, "%s: Can't unregister major %d\n", TCAP_DRV_NAME,
					tcap_majors[major]);
			if (major)
				tcap_majors[major] = 0;
		}
	}
	tcap_term_caches();
	return;
}

/*
 *  =========================================================================
 *
 *  LINUX MODULE INITIALIZATION
 *
 *  =========================================================================
 */
int init_module(void)
{
	tcap_init();
	if (tcap_initialized < 0)
		return tcap_initialized;
	return (0);
}
void cleanup_module(void)
{
	tcap_terminate();
}


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

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

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