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/mtp/mtp_min.c


File /code/strss7/drivers/mtp/mtp_min.c



#ident "@(#) $RCSfile: mtp_min.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/06/16 09:48:10 $"

static char const ident[] = "$RCSfile: mtp_min.c,v $ $Name:  $($Revision: 0.8.2.2 $) $Date: 2003/06/16 09:48:10 $";

/*
 *  This an MTP (Message Transfer Part) multiplexing driver which can have SL
 *  (Signalling Link) streams I_LINK'ed or I_PLINK'ed underneath it to form a
 *  complete Message Transfer Part protocol layer for SS7.  This is a minimal
 *  implementation which is suitable for GSM-A or F-Links only between SEPs.
 */

#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/sli.h>
#include <ss7/sli_ioctl.h>
#include <ss7/mtpi.h>
#include <ss7/mtpi_ioctl.h>

#include <sys/tpi.h>
#include <sys/tpi_sctp.h>
#include <sys/xti_mtp.h>

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

#define MTP_DESCRIP	"SS7 MESSAGE TRANSFER PART (MTP) STREAMS MULTIPLEXING DRIVER."
#define MTP_COPYRIGHT	"Copyright (c) 1997-2002 OpenSS7 Corporation.  All Rights Reserved."
#define MTP_DEVICE	"Part of the OpenSS7 Stack for LiS STREAMS."
#define MTP_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define MTP_LICENSE	"GPL"
#define MTP_BANNER	MTP_DESCRIP	"\n" \
			MTP_COPYRIGHT	"\n" \
			MTP_DEVICE	"\n" \
			MTP_CONTACT

MODULE_AUTHOR(MTP_CONTACT);
MODULE_DESCRIPTION(MTP_DESCRIP);
MODULE_SUPPORTED_DEVICE(MTP_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(MTP_LICENSE);
#endif

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

#define MTP_DRV_ID	0
#define MTP_DRV_NAME	"mtp-min"

static struct module_info mtp_winfo = {
	mi_idnum:MTP_DRV_ID,			/* Module ID number */
	mi_idname:MTP_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 mtp_rinfo = {
	mi_idnum:MTP_DRV_ID,			/* Module ID number */
	mi_idname:MTP_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 sl_winfo = {
	mi_idnum:MTP_DRV_ID,			/* Module ID number */
	mi_idname:MTP_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 sl_rinfo = {
	mi_idnum:MTP_DRV_ID,			/* Module ID number */
	mi_idname:MTP_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 mtp_open(queue_t *, dev_t *, int, int, cred_t *);
static int mtp_close(queue_t *, int, cred_t *);

static int mtp_rput(queue_t * q, mblk_t *);
static int mtp_rsrv(queue_t * q);

static struct qinit mtp_rinit = {
	qi_putp:mtp_rput,			/* Write put (message from below) */
	qi_srvp:mtp_rsrv,			/* Write queue service */
	qi_qopen:mtp_open,			/* Each open */
	qi_qclose:mtp_close,			/* Last close */
	qi_minfo:&mtp_rinfo,			/* Information */
};

static int mtp_wput(queue_t * q, mblk_t *);
static int mtp_wsrv(queue_t * q);

static struct qinit mtp_winit = {
	qi_putp:mtp_wput,			/* Write put (message from above) */
	qi_srvp:mtp_wsrv,			/* Write queue service */
	qi_minfo:&mtp_winfo,			/* Information */
};

static int sl_rput(queue_t * q, mblk_t *);
static int sl_rsrv(queue_t * q);

static struct qinit sl_rinit = {
	qi_putp:sl_rput,			/* Write put (message from below) */
	qi_srvp:sl_rsrv,			/* Write queue service */
	qi_minfo:&sl_rinfo,			/* Information */
};

static int sl_wput(queue_t * q, mblk_t *);
static int sl_wsrv(queue_t * q);

static struct qinit sl_winit = {
	qi_putp:sl_wput,			/* Write put (message from above) */
	qi_srvp:sl_wsrv,			/* Write queue service */
	qi_minfo:&sl_winfo,			/* Information */
};

static struct streamtab mtp_info = {
	st_rdinit:&mtp_rinit,			/* Upper read queue */
	st_wrinit:&mtp_winit,			/* Upper write queue */
	st_muxrinit:&sl_rinit,			/* Lower read queue */
	st_muxwinit:&sl_winit,			/* Lower write queue */
};

#define QR_DONE		0
#define QR_ABSORBED	1
#define QR_TRIMMED	2
#define QR_LOOP		3
#define QR_PASSALONG	4
#define QR_PASSFLOW	5
#define QR_DISABLE	6
#define QR_STRIP	7

/*
 *  =========================================================================
 *
 *  Private structures
 *
 *  =========================================================================
 */

typedef struct mtp_opts {
	uint flags;				/* success flags */
	t_uscalar_t *pvar;
	t_uscalar_t *mplev;
	t_uscalar_t *debug;
	t_uscalar_t *cluster;
	t_uscalar_t *sls;
	t_uscalar_t *mp;
} mtp_opts_t;

typedef struct mtp_options {
	uint flags;				/* success flags */
	t_uscalar_t pvar;
	t_uscalar_t mplev;
	t_uscalar_t debug;
	t_uscalar_t cluster;
	t_uscalar_t sls;
	t_uscalar_t mp;
} mtp_options_t;

static mtp_options_t mtp_opt_defaults = { SS7_PVAR_ETSI_00, 0, 0, 0, 0, 0 };

/* list linkage */
#define HEAD_DECLARATION(__type) \
	__type *next;			/* list linkage */ \
	__type **prev;			/* list linkage */ \
	size_t refcnt;			/* reference count */ \
	lis_spin_lock_t lock;		/* structure lock */ \
	ulong id;			/* structure id */ \


typedef struct head {
	HEAD_DECLARATION (struct head);		/* head declaration */
} head_t;

/* stream related structures */
#define STR_DECLARATION(__type) \
	HEAD_DECLARATION(__type);	/* list linkage */ \
	union { \
		struct { \
			ushort cmajor;	/* device major */ \
			ushort cminor;	/* device minor */ \
		} dev; \
		struct { \
			ulong index;	/* linked index */ \
		} mux; \
	} u; \
	queue_t *rq;			/* rd queue */ \
	queue_t *wq;			/* wr queue */ \
	lis_spin_lock_t qlock;		/* queue lock */ \
	uint rbid;			/* rd bufcall id */ \
	uint wbid;			/* wr bufcall id */ \
	queue_t *rwait;			/* rd queue waiting */ \
	queue_t *wwait;			/* wr queue waiting */ \
	ulong state;			/* interface state */ \
	ulong flags;			/* interface flags */ \
	ulong style;			/* interface style */ \
	ulong version			/* interface version */ \


/* generic stream structure */
typedef struct str {
	STR_DECLARATION (struct str);		/* stream declaration */
} str_t;
#define PRIV(__q) ((str_t *)(__q)->q_ptr)

struct mtp;					/* MTP User */
struct na;					/* Network Appearance */
struct sp;					/* Signalling Point (Local/Adjacent/Remote) */
struct rs;					/* Route Set */
struct rl;					/* Route List */
struct rt;					/* Route */
struct ls;					/* (Combined) Link Set */
struct lk;					/* Link (Set) */
struct sl;					/* Signalling Link */

/* mtp user */
typedef struct mtp {
	STR_DECLARATION (struct mtp);		/* stream declaration */
	struct na *na;				/* network appearance */
	struct mtp_addr src;			/* srce address */
	struct mtp_addr dst;			/* dest address */
	struct {
		struct sp *loc;			/* this mtp user for local signalling point */
		struct sp *rem;			/* this mtp user for remote signalling point */
		struct mtp *next;		/* next mtp user for local signalling point */
		struct mtp **prev;		/* prev mtp user for local signalling point */
	} sp;
	struct sl *sl;				/* signalling link */
	mtp_options_t options;
	mtp_opt_conf_mtp_t timers;		/* MTP timers */
	mtp_opt_conf_mtp_t config;		/* MTP configuration */
} mtp_t;
#define MTP_PRIV(__q) ((mtp_t *)(__q)->q_ptr)

static mtp_t *mtp_list = NULL;

/* network appearance */
typedef struct na {
	HEAD_DECLARATION (struct na);		/* head declaration */
	struct {
		uint32_t member;		/* pc member mask */
		uint32_t cluster;		/* pc cluster mask */
		uint32_t network;		/* pc network mask */
	} mask;
	lmi_option_t option;			/* protocol variant and options */
	struct T_info_ack *prot[16];		/* protoocl profiles for si values */
} na_t;

static ulong na_index = 0;
static na_t *na_list = NULL;

/* route set */
typedef struct rs {
	HEAD_DECLARATION (struct rs);		/* head declaration */
	int type;				/* type of routeset (cluster, member) */
	int cong_status;			/* congestion status */
	int disc_status;			/* discard status */
	uint32_t dest;				/* remote signalling point/cluster */
	struct {
		struct sp *sp;			/* this routeset for this signalling point */
		struct rs *next;		/* prev routeset for this signalling point */
		struct rs **prev;		/* next routeset for this signalling point */
	} sp;
	struct {
		size_t numb;			/* numb of route lists in this routeset */
		struct rl *list;		/* list of route lists in this routeset */
		struct rl *rl;			/* current route list for this routeset */
	} rl;
	mtp_opt_conf_rs_t timers;		/* RS timers */
	mtp_opt_conf_rs_t config;		/* RS configuration */
} rs_t;

static ulong rs_index = 0;
static rs_t *rs_list = NULL;

/* route list */
typedef struct rl {
	HEAD_DECLARATION (struct rl);		/* head declaration */
	ulong cost;				/* priority of this route list */
	struct {
		struct sp *sp;			/* signalling point for this route list */
	} sp;
	struct {
		ulong cost;			/* cost of route list in routset */
		struct rs *rs;			/* this route list for this route set */
		struct rl *next;		/* next route list for this route set */
		struct rl **prev;		/* prev route list for this route set */
	} rs;
	struct {
		size_t numb;			/* numb of routes in this route list */
		struct rt *list;		/* list of routes in this route list */
		size_t actv;			/* active routes in this route list */
		struct rt *smap[4];		/* smap of routes in this route list */
	} rt;
	struct {
		struct ls *ls;			/* this route list for this linkset */
		struct rl *next;		/* next route list for this linkset */
		struct rl **prev;		/* prev route list for this linkset */
	} ls;
} rl_t;

static ulong rl_index = 0;
static rl_t *rl_list = NULL;

/* route */
typedef struct rt {
	HEAD_DECLARATION (struct rt);		/* head declaration */
	struct {
		struct sp *sp;			/* signalling point for this route */
	} sp;
	struct {
		struct rl *rl;			/* this route in this route list */
		struct rt *next;		/* next route in this route list */
		struct rt **prev;		/* prev route in this route list */
	} rl;
	struct {
		struct lk *lk;			/* this route to this link */
		struct rt *next;		/* next route to this link */
		struct rt **prev;		/* prev route to this link */
	} lk;
	struct bufq *buf;			/* time controlled rerouting buffer */
	mtp_opt_conf_rt_t timers;		/* RT timers */
	mtp_opt_conf_rt_t config;		/* RT configuration */
} rt_t;

static ulong rt_index = 0;
static rt_t *rt_list = NULL;

/* signalling point */
typedef struct sp {
	HEAD_DECLARATION (struct sp);		/* head declaration */
	uint type;				/* point code type */
	uint32_t ni;				/* network indicator */
	uint32_t pc;				/* point code */
	uint sls;				/* sls for loadsharing of management messages */
	struct na *na;				/* associated network appearance */
	struct {
		ushort equipped;		/* bit mask of equipped users */
		ushort available;		/* bit mask of available users */
		struct mtp *lists[16];		/* lists of MTP users */
	} mtp;
	struct {
		size_t numb;			/* numb of route sets */
		struct rs *list;		/* list of route sets */
		struct rs **hash;		/* hash of route sets */
	} rs;
	struct {
		size_t numb;			/* numb of linksets */
		struct ls *list;		/* list of linksets */
	} ls;
	mtp_opt_conf_sp_t timers;		/* SP timers */
	mtp_opt_conf_sp_t config;		/* SP configuration */
} sp_t;

static ulong sp_index = 0;
struct sp *sp_list = NULL;

/* link set */
typedef struct ls {
	HEAD_DECLARATION (struct ls);		/* head declaration */
	struct {
		struct sp *sp;			/* this link set on this signalling point */
		struct ls *next;		/* next link set on this signalling point */
		struct ls **prev;		/* prev link set on this signalling point */
	} sp;
	struct {
		struct rl *list;		/* list of route lists using this linkset */
	} rl;
	struct {
		struct lk *list;		/* list of links in this linkset */
		uint sls_mask;			/* mask for selecting links in this linkset */
	} lk;
	struct sl **sls_map;			/* sls map for link set */
} ls_t;

static ulong ls_index = 0;
static ls_t *ls_list = NULL;

/* link */
typedef struct lk {
	HEAD_DECLARATION (struct lk);		/* head declaration */
	struct {
		struct sp *sp;			/* signalling point for this link */
	} sp;
	struct {
		struct ls *ls;			/* this link in this linkset */
		struct lk *next;		/* next link in this linkset */
		struct lk **prev;		/* prev link in this linkset */
		uint sls_slot;			/* sls slot for this linkset */
	} ls;
	struct {
		struct rt *list;		/* list of routes using this link */
	} rt;
	struct {
		struct sl *list;		/* list of signalling links in this link */
		struct sl **smap;		/* sls map of signalling links in this link */
		uint smap_mask;			/* mask of sls for sls map */
	} sl;
	ulong ni;				/* network indicator for link */
	struct sp *loc;				/* local signalling point */
	struct sp *adj;				/* adjacent signalling point */
	mtp_opt_conf_lk_t timers;		/* link timers */
	mtp_opt_conf_lk_t config;		/* link configuration */
} lk_t;

static ulong lk_index = 0;
static lk_t *lk_list = NULL;

/* signalling link */
typedef struct sl {
	STR_DECLARATION (struct sl);		/* stream declaration */
	struct {
		struct sp *loc;			/* local signalling point for this signalling link */
		struct sp *adj;			/* adjacent signalling point for this signalling link */
	} sp;
	struct {
		struct lk *lk;			/* this signalling link in this link */
		struct sl *next;		/* next signalling link in this link */
		struct sl **prev;		/* prev signalling link in this link */
	} lk;
	ulong slc;				/* signalling link code */
	int cong_status;			/* congestion status */
	int disc_status;			/* discard status */
	struct bufq buff;			/* retrieval buffer */
	uint fsnc;				/* retreived BSNT */
	uint reason;				/* reason for failure */
	unsigned char tdata[16];		/* test data */
	size_t tlen;				/* test length */
	mtp_opt_conf_sl_t timers;		/* SL timers */
	mtp_opt_conf_sl_t config;		/* SL configuration */
} sl_t;

static sl_t *sl_list = NULL;
#define SL_PRIV(__q) ((sl_t *)(__q)->q_ptr)

/*
 *  =========================================================================
 *
 *  Locking
 *
 *  =========================================================================
 */
static int mtp_trylock(queue_t * q)
{
	int res;
	str_t *s = PRIV(q);
	if (!(res = lis_spin_trylock(&s->qlock))) {
		if (q == s->rq)
			s->rwait = q;
		if (q == s->wq)
			s->wwait = q;
	}
	return (res);
}
static void mtp_unlockq(queue_t * q)
{
	str_t *s = PRIV(q);
	lis_spin_unlock(&s->qlock);
	if (s->rwait)
		qenable(xchg(&s->rwait, NULL));
	if (s->wwait)
		qenable(xchg(&s->wwait, NULL));
}

/*
 *  =========================================================================
 *
 *  Buffer Allocation
 *
 *  =========================================================================
 */
typedef void (*bufcall_fnc_t) (long);
/*
 *  BUFSRV calls service routine
 *  -------------------------------------------------------------------------
 */
static void mtp_bufsrv(long data)
{
	queue_t *q = (queue_t *) data;
	if (q) {
		str_t *s = PRIV(q);
		if (q == s->rq) {
			if (s->rbid) {
				s->rbid = 0;
				s->refcnt--;
			}
		}
		if (q == s->wq) {
			if (s->wbid) {
				s->wbid = 0;
				s->refcnt--;
			}
		}
		qenable(q);
	}
}

/*
 *  UNBUFCALL
 *  -------------------------------------------------------------------------
 */
static void mtp_unbufcall(queue_t * q)
{
	str_t *s = PRIV(q);
	if (s->rbid) {
		unbufcall(xchg(&s->rbid, 0));
		s->refcnt--;
	}
	if (s->wbid) {
		unbufcall(xchg(&s->wbid, 0));
		s->refcnt--;
	}
}

/*
 *  ALLOCB
 *  -------------------------------------------------------------------------
 */
static mblk_t *mtp_allocb(queue_t * q, size_t size, int prior)
{
	mblk_t *mp;
	if ((mp = allocb(size, prior)))
		return (mp);
	rare();
	if (q) {
		str_t *s = PRIV(q);
		if (q == s->rq) {
			if (!s->rbid) {
				s->rbid = bufcall(size, prior, &mtp_bufsrv, (long) q);
				s->refcnt++;
			}
			return (NULL);
		}
		if (q == s->wq) {
			if (!s->wbid) {
				s->wbid = bufcall(size, prior, &mtp_bufsrv, (long) q);
				s->refcnt++;
			}
			return (NULL);
		}
		swerr();
		return (NULL);
	}
	return (NULL);
}

/*
 *  ALLOCB
 *  -------------------------------------------------------------------------
 */
static mblk_t *mtp_esballoc(queue_t * q, unsigned char *base, size_t size, int prior, frtn_t * frtn)
{
	mblk_t *mp;
	if ((mp = esballoc(base, size, prior, frtn)))
		return (mp);
	rare();
	if (q) {
		str_t *s = PRIV(q);
		if (q == s->rq) {
			if (!s->rbid) {
				s->rbid = esbbcall(prior, &mtp_bufsrv, (long) q);
				s->refcnt++;
			}
			return (NULL);
		}
		if (q == s->wq) {
			if (!s->wbid) {
				s->wbid = esbbcall(prior, &mtp_bufsrv, (long) q);
				s->refcnt++;
			}
			return (NULL);
		}
		swerr();
		return (NULL);
	}
	return (NULL);
}

/*
 *  =========================================================================
 *
 *  OPTION Handling
 *
 *  =========================================================================
 */
#ifndef _T_ALIGN_SIZE
#define _T_ALIGN_SIZE sizeof(t_uscalar_t)
#endif
#ifndef _T_ALIGN_SIZEOF
#define _T_ALIGN_SIZEOF(s) \
	((sizeof((s)) + _T_ALIGN_SIZE - 1) & ~(_T_ALIGN_SIZE - 1))
#endif
static size_t mtp_opts_size(mtp_t * m, mtp_opts_t * ops)
{
	size_t len = 0;
	if (ops) {
		const size_t hlen = sizeof(struct t_opthdr);	/* 32 bytes */
		if (ops->pvar)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->pvar));
		if (ops->mplev)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->mplev));
		if (ops->debug)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->debug));
		if (ops->cluster)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->cluster));
		if (ops->sls)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->sls));
		if (ops->mp)
			len += hlen + _T_ALIGN_SIZEOF(*(ops->mp));
	}
	return (len);
}
static void mtp_build_opts(mtp_t * m, mtp_opts_t * ops, unsigned char *p)
{
	if (ops) {
		struct t_opthdr *oh;
		const size_t hlen = sizeof(struct t_opthdr);
		if (ops->pvar) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->pvar));
			oh->level = T_SS7_MTP;
			oh->name = T_MTP_PVAR;
			oh->status = (ops->flags & (1 << T_MTP_PVAR)) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->pvar)) p) = *(ops->pvar);
			p += _T_ALIGN_SIZEOF(*ops->pvar);
		}
		if (ops->mplev) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->mplev));
			oh->level = T_SS7_MTP;
			oh->name = T_MTP_MPLEV;
			oh->status = (ops->flags & (1 << T_MTP_MPLEV)) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->mplev)) p) = *(ops->mplev);
			p += _T_ALIGN_SIZEOF(*ops->mplev);
		}
		if (ops->debug) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->debug));
			oh->level = T_SS7_MTP;
			oh->name = T_MTP_DEBUG;
			oh->status = (ops->flags & (1 << T_MTP_DEBUG)) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->debug)) p) = *(ops->debug);
			p += _T_ALIGN_SIZEOF(*ops->debug);
		}
		if (ops->cluster) {
			oh = ((typeof(oh)) p)++;
			oh->len = hlen + sizeof(*(ops->cluster));
			oh->level = T_SS7_MTP;
			oh->name = T_MTP_CLUSTER;
			oh->status = (ops->flags & (1 << T_MTP_CLUSTER)) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->cluster)) p) = *(ops->cluster);
			p += _T_ALIGN_SIZEOF(*ops->cluster);
		}
		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 & (1 << T_MTP_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 & (1 << T_MTP_MP)) ? T_SUCCESS : T_FAILURE;
			*((typeof(ops->mp)) p) = *(ops->mp);
			p += _T_ALIGN_SIZEOF(*ops->mp);
		}
	}
}
static int mtp_parse_opts(mtp_t * m, mtp_opts_t * ops, unsigned char *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_MTP:
			switch (oh->name) {
			case T_MTP_PVAR:
				ops->pvar = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= (1 << T_MTP_PVAR);
				continue;
			case T_MTP_MPLEV:
				ops->mplev = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= (1 << T_MTP_MPLEV);
				continue;
			case T_MTP_DEBUG:
				ops->debug = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= (1 << T_MTP_DEBUG);
				continue;
			case T_MTP_CLUSTER:
				ops->cluster = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= (1 << T_MTP_CLUSTER);
				continue;
			case T_MTP_SLS:
				ops->sls = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= (1 << T_MTP_SLS);
				continue;
			case T_MTP_MP:
				ops->mp = (void *) _T_OPT_DATA_OFS(oh, 0);
				ops->flags |= (1 << T_MTP_MP);
				continue;
			}
		}
	}
	if (oh)
		return (TBADOPT);
	return (0);
}

/*
 *  =========================================================================
 *
 *  OPTIONS handling
 *
 *  =========================================================================
 */
static int mtp_opt_check(mtp_t * m, mtp_opts_t * ops)
{
	if (ops->flags) {
		ops->flags = 0;
		if (ops->pvar)
			ops->flags |= (1 << T_MTP_PVAR);
		if (ops->mplev)
			ops->flags |= (1 << T_MTP_MPLEV);
		if (ops->debug)
			ops->flags |= (1 << T_MTP_DEBUG);
		if (ops->cluster)
			ops->flags |= (1 << T_MTP_CLUSTER);
		if (ops->sls)
			ops->flags |= (1 << T_MTP_SLS);
		if (ops->mp)
			ops->flags |= (1 << T_MTP_MP);
	}
	return (0);
}
static int mtp_opt_default(mtp_t * m, mtp_opts_t * ops)
{
	if (ops) {
		int flags = ops->flags;
		ops->flags = 0;
		if (!flags || ops->pvar) {
			ops->pvar = &mtp_opt_defaults.pvar;
			ops->flags |= (1 << T_MTP_PVAR);
		}
		if (!flags || ops->mplev) {
			ops->mplev = &mtp_opt_defaults.mplev;
			ops->flags |= (1 << T_MTP_MPLEV);
		}
		if (!flags || ops->debug) {
			ops->debug = &mtp_opt_defaults.debug;
			ops->flags |= (1 << T_MTP_DEBUG);
		}
		if (!flags || ops->cluster) {
			ops->cluster = &mtp_opt_defaults.cluster;
			ops->flags |= (1 << T_MTP_CLUSTER);
		}
		if (!flags || ops->sls) {
			ops->sls = &mtp_opt_defaults.sls;
			ops->flags |= (1 << T_MTP_SLS);
		}
		if (!flags || ops->mp) {
			ops->mp = &mtp_opt_defaults.mp;
			ops->flags |= (1 << T_MTP_MP);
		}
		return (0);
	}
	swerr();
	return (-EFAULT);
}
static int mtp_opt_current(mtp_t * m, mtp_opts_t * ops)
{
	int flags = ops->flags;
	ops->flags = 0;
	if (!flags || ops->pvar) {
		ops->pvar = &m->options.pvar;
		ops->flags |= (1 << T_MTP_PVAR);
	}
	if (!flags || ops->mplev) {
		ops->mplev = &m->options.mplev;
		ops->flags |= (1 << T_MTP_PVAR);
	}
	if (!flags || ops->debug) {
		ops->debug = &m->options.debug;
		ops->flags |= (1 << T_MTP_PVAR);
	}
	if (!flags || ops->cluster) {
		ops->cluster = &m->options.cluster;
		ops->flags |= (1 << T_MTP_PVAR);
	}
	if (!flags || ops->sls) {
		ops->sls = &m->options.sls;
		ops->flags |= (1 << T_MTP_PVAR);
	}
	if (!flags || ops->mp) {
		ops->mp = &m->options.mp;
		ops->flags |= (1 << T_MTP_PVAR);
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  STATE Changes
 *
 *  =========================================================================
 */
#ifdef _DEBUG
static const char *state_name(long 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");
	case TS_NOSTATES:
		return ("TS_NOSTATES");
	default:
		return ("(unknown)");
	}
}
#endif
static void mtp_set_state(mtp_t * m, long state)
{
	printd(("%s: %p: %s <- %s\n", MTP_DRV_NAME, m, state_name(state), state_name(m->state)));
	m->state = state;
}
static long mtp_get_state(mtp_t * m)
{
	return (m->state);
}

/*
 *  =========================================================================
 *
 *  MTP Functions
 *
 *  =========================================================================
 */
/*
 *  CHECK SRC (BIND) ADDRESS
 *  -----------------------------------
 *  Check the source (bind) address. This must be an address local to the
 *  stack and the bind must be for an SI value which is not alreay bound
 *  (T_CLTS).
 */
static int mtp_check_src(mtp_t * m, mtp_addr_t * src)
{
	sl_t *sl;
	for (sl = sl_list; sl; sl = sl->next)
		if (sl->ni == src->mtp_ni && sl->loc == src->mtp_pc)
			goto check;
	goto noaddr;
      check:
	if (sl->users[src->mtp_si & 0xff])
		goto addrbusy;
	return (0);
      noaddr:
	printd(("%s: %p: ERROR: Couldn't allocate source address\n", MTP_DRV_NAME, m));
	return (TNOADDR);
      addrbusy:
	printd(("%s: %p: ERROR: Source address in use\n", MTP_DRV_NAME, m));
	return (TADDRBUSY);
}

/*
 *  CHECK DST (CONN) ADDRESS
 *  -----------------------------------
 *  Check the destination (connect) address.  This may be a local, adjacent or
 *  remote address and the connection must be for a destination point code and
 *  SI value which is not already connected.
 */
static int mtp_check_dst(mtp_t * m, mtp_addr_t * dst)
{
	sl_t *sl;
	if (!(sl = m->sl))
		goto noaddr;
	if (sl->adj != dst->mtp_pc)
		goto noaddr;
	if (!sl->users[dst->mtp_si & 0xff])
		goto noaddr;
	if (sl->users[dst->mtp_si & 0xff] != m)
		goto addrbusy;
	return (0);
      noaddr:
	printd(("%s: %p: ERROR: Couldn't allocate destination address\n", MTP_DRV_NAME, m));
	return (TNOADDR);
      addrbusy:
	printd(("%s: %p: ERROR: Destination address in use\n", MTP_DRV_NAME, m));
	return (TADDRBUSY);
}

/*
 *  MTP BIND
 *  -------------------------------------------------------------------------
 *  Add the MTP user to the local signalling point hashes if T_CLTS.
 */
static int mtp_bind(mtp_t * m, mtp_addr_t * src)
{
	sl_t *sl;
	for (sl = sl_list; sl; sl = sl->next)
		if (sl->ni == src->mtp_ni && sl->loc == src->mtp_pc)
			break;
	if (sl) {
		sl->users[src->mtp_si & 0xff] = m;
		m->src = *src;
		m->sl = sl;
		return (0);
	}
	swerr();
	return (-EFAULT);
}

/*
 *  MTP UNBIND
 *  -------------------------------------------------------------------------
 *  Remove the MTP user from the local signalling point hashes if T_CLTS.
 */
static int mtp_unbind(mtp_t * m)
{
	sl_t *sl;
	if ((sl = m->sl)) {
		sl->users[m->src.mtp_si & 0xff] = NULL;
		m->sl = NULL;
		bzero(&m->src, sizeof(m->src));
		return (0);
	}
	swerr();
	return (-EFAULT);
}

/*
 *  MTP CONNECT
 *  -------------------------------------------------------------------------
 *  Add the MTP user to the local signalling point service hashes.
 */
static int mtp_connect(mtp_t * m, mtp_addr_t * dst)
{
	m->dst = *dst;
	return (0);
}

/*
 *  MTP DISCONNECT
 *  -------------------------------------------------------------------------
 *  Remove the MTP user from the local signalling point service hashes.
 */
static int mtp_disconnect(mtp_t * m)
{
	bzero(&m->dst, sizeof(m->dst));
	return (0);
}

/*
 *  MTP SEND MSG
 *  -------------------------------------------------------------------------
 */
static int tp_send_msg(mtp_t * m, mtp_opts_t * opt, mtp_addr_t * dst, mblk_t * dp)
{
	fixme(("Send message\n"));
	return (-EFAULT);
}

/*
 *  =========================================================================
 *
 *  PRIMITIVES
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  Primitive sent upstream
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  M_ERROR
 *  -----------------------------------
 */
static int m_error(queue_t * q, int error)
{
	mtp_t *m = MTP_PRIV(q);
	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 = mtp_allocb(q, 2, BPRI_MED))) {
		if (hangup) {
			mp->b_datap->db_type = M_HANGUP;
			printd(("%s: %p: <- M_HANGUP\n", MTP_DRV_NAME, m));
			putnext(m->rq, mp);
			return (-error);
		} else {
			mp->b_datap->db_type = M_ERROR;
			*(mp->b_wptr)++ = error < 0 ? -error : error;
			*(mp->b_wptr)++ = error < 0 ? -error : error;
			mtp_set_state(m, TS_NOSTATES);
			printd(("%s; %p: <- M_ERROR\n", MTP_DRV_NAME, m));
			putnext(m->rq, mp);
			return (QR_DONE);
		}
	}
	rare();
	return (-ENOBUFS);
}

static int t_error_ack(queue_t * q, ulong prim, long error);

/*
 *  T_INFO_ACK
 *  -----------------------------------
 */
static int t_info_ack(queue_t * q)
{
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_info_ack *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_INFO_ACK;
		p->TSDU_size = 272;
		p->ETSDU_size = T_INVALID;
		p->CDATA_size = T_INVALID;
		p->DDATA_size = T_INVALID;
		p->ADDR_size = sizeof(mtp_addr_t);
		p->OPT_size = T_INFINITE;
		p->SERV_type = T_CLTS;
		p->CURRENT_state = m->state;
		p->PROVIDER_flag = XPG4_1 & ~T_SNDZERO;
		printd(("%s: %p: <- T_INFO_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
}

/*
 *  T_BIND_ACK
 *  -----------------------------------
 */
static int t_bind_ack(queue_t * q, mtp_addr_t * add)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	mblk_t *mp;
	struct T_bind_ack *p;
	size_t add_len = add ? sizeof(*add) : 0;
	if ((mp = mtp_allocb(q, sizeof(*p) + add_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_INFO_ACK;
		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;
		}
		if ((err = mtp_bind(m, add)))
			goto free_error;
		mtp_set_state(m, TS_IDLE);
		printd(("%s: %p: <- T_BIND_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
      free_error:
	freemsg(mp);
	return t_error_ack(q, T_BIND_REQ, err);
}

/*
 *  T_ERROR_ACK
 *  -----------------------------------
 */
static int t_error_ack(queue_t * q, ulong prim, long error)
{
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_error_ack *p;
	switch (error) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
		seldom();
		return (error);
	case 0:
		never();
		return (error);
	}
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		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;
		if (error != TOUTSTATE) {
			switch (mtp_get_state(m)) {
#ifdef TS_WACK_OPTREQ
			case TS_WACK_OPTREQ:
				mtp_set_state(m, TS_IDLE);
				break;
#endif
			case TS_WACK_UREQ:
				mtp_set_state(m, TS_IDLE);
				break;
			case TS_WACK_BREQ:
				mtp_set_state(m, TS_UNBND);
				break;
			default:
				/* 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
				   TS_IDLE state. */
				break;
			}
		}
		printd(("%s: %p: <- T_ERROR_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		/* Retruning -EPROTO here will make sure that the old state is restored correctly. If we return
		   QR_DONE, then the state will never be restored. */
		if (error < 0)
			return (error);
		return (-EPROTO);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
}

/*
 *  T_OK_ACK
 *  -----------------------------------
 */
static int t_ok_ack(queue_t * q, ulong prim)
{
	int err = -EFAULT;
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_ok_ack *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_OK_ACK;
		p->CORRECT_prim = prim;
		switch (mtp_get_state(m)) {
#if 0
		case TS_WACK_CREQ:
			if ((err = mtp_connect(m, &m->dst)))
				goto free_error;
			mtp_set_state(m, TS_WCON_CREQ);
			if ((err = t_conn_con(m, &m->dst)))
				goto free_error;
			mtp_set_state(m, TS_DATA_XFER);
			break;
#endif
		case TS_WACK_UREQ:
			if ((err = mtp_unbind(m)))
				goto free_error;
			mtp_set_state(m, TS_UNBND);
			break;
		case TS_WACK_DREQ6:
		case TS_WACK_DREQ9:
			if ((err = mtp_disconnect(m)))
				goto free_error;
			mtp_set_state(m, TS_IDLE);
			break;
		default:
			/* 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 TS_IDLE state. */
			break;
		}
		printd(("%s: %p: <- T_OK_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
      free_error:
	freemsg(mp);
	return t_error_ack(q, prim, err);
}

/*
 *  T_UNITDATA_IND
 *  -----------------------------------
 */
static int t_unitdata_ind(queue_t * q, mtp_addr_t * src, mtp_opts_t * opt, mblk_t * dp)
{
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_unitdata_ind *p;
	size_t src_len = src ? sizeof(*src) : 0;
	size_t opt_len = opt ? mtp_opts_size(m, opt) : 0;
	if ((mp = mtp_allocb(q, sizeof(*p) + src_len + opt_len, BPRI_MED))) {
		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) {
			mtp_build_opts(m, opt, mp->b_wptr);
			mp->b_wptr += opt_len;
		}
		mp->b_cont = dp;
		printd(("%s: %p: <- T_UNITDATA_IND\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
}

/*
 *  T_UDERROR_IND
 *  -----------------------------------
 *  This primitive indicatest to the MTP user that a message with the
 *  specified destination address and options produced an error.
 */
static int t_uderror_ind(queue_t * q, mtp_t * m, mtp_addr_t * dst, mtp_opts_t * opt, mblk_t * dp, int etype)
{
	mblk_t *mp;
	struct T_uderror_ind *p;
	size_t dst_len = dst ? sizeof(*dst) : 0;
	size_t opt_len = opt ? mtp_opts_size(m, opt) : 0;
	if (canputnext(m->rq)) {
		if ((mp = mtp_allocb(q, sizeof(*p) + dst_len + opt_len, BPRI_MED))) {
			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) {
				mtp_build_opts(m, opt, mp->b_wptr);
				mp->b_wptr += opt_len;
			}
			mp->b_cont = dp;
			printd(("%s: %p: <- T_UDERROR_IND\n", MTP_DRV_NAME, m));
			putnext(m->rq, mp);
			return (QR_DONE);
		}
		ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
		return (-ENOBUFS);
	}
	ptrace(("%s: %p: ERROR: Flow controlled\n", MTP_DRV_NAME, m));
	return (-EBUSY);
}

/*
 *  T_OPTMGMT_ACK
 *  -----------------------------------
 */
static int t_optmgmt_ack(queue_t * q, ulong flags, mtp_opts_t * opt)
{
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_optmgmt_ack *p;
	size_t opt_len = opt ? mtp_opts_size(m, opt) : 0;
	if ((mp = mtp_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		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;
		if (opt_len) {
			mtp_build_opts(m, opt, mp->b_wptr);
			mp->b_wptr += opt_len;
		}
#ifdef TS_WACK_OPTREQ
		if (mtp_get_state(m) == TS_WACK_OPTREQ)
			mtp_set_state(m, TS_IDLE);
#endif
		printd(("%s: %p: <- T_OPTMGMT_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
}

#ifdef T_ADDR_ACK
/*
 *  T_ADDR_ACK
 *  -----------------------------------
 */
static int t_addr_ack(queue_t * q, mtp_addr_t * loc, mtp_addr_t * rem)
{
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_addr_ack *p;
	size_t loc_len = loc ? sizeof(*loc) : 0;
	size_t rem_len = rem ? sizeof(*rem) : 0;
	if ((mp = mtp_allocb(q, sizeof(*p) + loc_len + rem_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) 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) : 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;
		}
		printd(("%s: %p: <- T_ADDR_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
}
#endif
#ifdef T_CAPABILITY_ACK
/*
 *  T_CAPABILITY_ACK
 *  -----------------------------------
 */
static int t_capability_ack(queue_t * q, ulong caps)
{
	mtp_t *m = MTP_PRIV(q);
	mblk_t *mp;
	struct T_capability_ack *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->PRIM_type = T_CAPABILITY_ACK;
		p->CAP_bits1 = TC1_INFO;
		p->ACCEPTOR_id = (caps & TC1_ACCEPTOR) ? (ulong) m->rq : 0;
		if (caps & TC1_INFO) {
			p->INFO_ack->PRIM_type = T_INFO_ACK;
			p->INFO_ack->TSDU_size = 272;
			p->INFO_ack->ETSDU_size = T_INVALID;
			p->INFO_ack->CDATA_size = T_INVALID;
			p->INFO_ack->DDATA_size = T_INVALID;
			p->INFO_ack->ADDR_size = sizeof(mtp_addr_t);
			p->INFO_ack->OPT_size = T_INFINITE;
			p->INFO_ack->SERV_type = T_CLTS;
			p->INFO_ack->CURRENT_state = m->state;
			p->INFO_ack->PROVIDER_flag = XPG4_1 & ~T_SNDZERO;
		} else
			bzero(&p->INFO_ack, sizeof(p->INFO_ack));
		printd(("%s: %p: <- T_CAPABILITY_ACK\n", MTP_DRV_NAME, m));
		putnext(m->rq, mp);
		return (QR_DONE);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, m));
	return (-ENOBUFS);
}
#endif
/*
 *  -------------------------------------------------------------------------
 *
 *  Primitive sent downstream
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  SL_PDU_REQ
 *  -----------------------------------
 */
static int sl_pdu_req(queue_t * q, mblk_t * dp)
{
	sl_t *sl = SL_PRIV(q);
	if (canputnext(sl->wq)) {
		mblk_t *mp;
		sl_pdu_req_t *p;
		if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
			mp->b_datap->db_type = M_PROTO;
			p = ((typeof(p)) mp->b_wptr)++;
			p->sl_primitive = SL_PDU_REQ;
			p->sl_mp = 0;
			mp->b_cont = dp;
			printd(("%s: %p: SL_PDU_REQ [%d] ->\n", MTP_DRV_NAME, sl, msgdsize(dp)));
			putnext(sl->wq, mp);
			return (QR_ABSORBED);
		}
		ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
		return (-ENOBUFS);
	}
	ptrace(("%s: %p: ERROR: flow controlled\n", MTP_DRV_NAME, sl));
	return (-EBUSY);
}

/*
 *  SL_EMERGENCY_REQ
 *  -----------------------------------
 */
static int sl_emergency_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_emergency_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_EMERGENCY_REQ;
		printd(("%s: %p: SL_EMERGENCY_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_EMERGENCY_CEASES_REQ
 *  -----------------------------------
 */
static int sl_emergency_ceases_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_emergency_ceases_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_EMERGENCY_CEASES_REQ;
		printd(("%s: %p: SL_EMERGENCY_CEASES_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_START_REQ
 *  -----------------------------------
 */
static int sl_start_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_start_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_START_REQ;
		printd(("%s: %p: SL_START_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_STOP_REQ
 *  -----------------------------------
 */
static int sl_stop_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_stop_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_STOP_REQ;
		printd(("%s: %p: SL_STOP_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_RETRIEVE_BSNT_REQ
 *  -----------------------------------
 */
static int sl_retrieve_bsnt_req(queue_t * q, sl_t * sl)
{
	mblk_t *mp;
	sl_retrieve_bsnt_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_RETRIEVE_BSNT_REQ;
		printd(("%s: %p: SL_RETRIEVE_BSNT_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_RETRIEVAL_REQUEST_AND_FSNC_REQ
 *  -----------------------------------
 */
static int sl_retrieval_request_and_fsnc_req(queue_t * q, ulong fsnc)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_retrieval_req_and_fsnc_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_RETRIEVAL_REQUEST_AND_FSNC_REQ;
		p->sl_fsnc = fsnc;
		printd(("%s: %p: SL_RETRIEVAL_REQUEST_AND_FSNC_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_RESUME_REQ
 *  -----------------------------------
 */
static int sl_resume_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_resume_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_RESUME_REQ;
		printd(("%s: %p: SL_RESUME_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_CLEAR_BUFFERS_REQ
 *  -----------------------------------
 */
static int sl_clear_buffers_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_clear_buffers_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_CLEAR_BUFFERS_REQ;
		printd(("%s: %p: SL_CLEAR_BUFFERS_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_CLEAR_RTB_REQ
 *  -----------------------------------
 */
static int sl_clear_rtb_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_clear_rtb_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_CLEAR_RTB_REQ;
		printd(("%s: %p: SL_CLEAR_RTB_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_LOCAL_PROCESSOR_OUTAGE_REQ
 *  -----------------------------------
 */
static int sl_local_processor_outage_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_local_proc_outage_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_LOCAL_PROCESSOR_OUTAGE_REQ;
		printd(("%s: %p: SL_LOCAL_PROCESSOR_OUTAGE_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_CONGESTION_DISCARD_REQ
 *  -----------------------------------
 */
static int sl_congestion_discard_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_cong_discard_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_CONGESTION_DISCARD_REQ;
		printd(("%s: %p: SL_CONGESTION_DISCARD_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_CONGESTION_ACCEPT_REQ
 *  -----------------------------------
 */
static int sl_congestion_accept_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_cong_accept_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_CONGESTION_ACCEPT_REQ;
		printd(("%s: %p: SL_CONGESTION_ACCEPT_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_NO_CONGESTION_REQ
 *  -----------------------------------
 */
static int sl_no_congestion_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_no_cong_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_NO_CONGESTION_REQ;
		printd(("%s: %p: SL_NO_CONGESTION_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_POWER_ON_REQ
 *  -----------------------------------
 */
static int sl_power_on_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_power_on_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_POWER_ON_REQ;
		printd(("%s: %p: SL_POWER_ON_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

#if 0
/*
 *  SL_OPTMGMT_REQ
 *  -----------------------------------
 */
static int sl_optmgmt_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_optmgmt_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_OPTMGMT_REQ;
		printd(("%s: %p: SL_OPTMGMT_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  SL_NOTIFY_REQ
 *  -----------------------------------
 */
static int sl_notify_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	sl_notify_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->sl_primitive = SL_NOTIFY_REQ;
		printd(("%s: %p: SL_NOTIFY_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}
#endif
/*
 *  LMI_INFO_REQ
 *  -----------------------------------
 */
static int lmi_info_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_info_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_INFO_REQ;
		printd(("%s: %p: LMI_INFO_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  LMI_ATTACH_REQ
 *  -----------------------------------
 */
static int lmi_attach_req(queue_t * q, caddr_t ppa_ptr, size_t ppa_len)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_attach_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p) + ppa_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_ATTACH_REQ;
		if (ppa_ptr && ppa_len) {
			bcopy(ppa_ptr, mp->b_wptr, ppa_len);
			mp->b_wptr += ppa_len;
		}
		printd(("%s: %p: LMI_ATTACH_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  LMI_DETACH_REQ
 *  -----------------------------------
 */
static int lmi_detach_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_detach_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_DETACH_REQ;
		printd(("%s: %p: LMI_DETACH_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  LMI_ENABLE_REQ
 *  -----------------------------------
 */
static int lmi_enable_req(queue_t * q, caddr_t dst_ptr, size_t dst_len)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_enable_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p) + dst_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_ENABLE_REQ;
		if (dst_ptr && dst_len) {
			bcopy(dst_ptr, mp->b_wptr, dst_len);
			mp->b_wptr += dst_len;
		}
		printd(("%s: %p: LMI_ENABLE_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  LMI_DISABLE_REQ
 *  -----------------------------------
 */
static int lmi_disable_req(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_disable_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p), BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_DISABLE_REQ;
		printd(("%s: %p: LMI_DISABLE_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  LMI_OPTMGMT_REQ
 *  -----------------------------------
 */
static int lmi_optmgmt_req(queue_t * q, ulong flags, caddr_t opt_ptr, size_t opt_len)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *mp;
	lmi_optmgmt_req_t *p;
	if ((mp = mtp_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		mp->b_datap->db_type = M_PCPROTO;
		p = ((typeof(p)) mp->b_wptr)++;
		p->lmi_primitive = LMI_OPTMGMT_REQ;
		p->lmi_opt_length = opt_len;
		p->lmi_opt_offset = opt_len ? sizeof(*p) : 0;
		p->lmi_mgmt_flags = flags;
		if (opt_ptr && opt_len) {
			bcopy(opt_ptr, mp->b_wptr, opt_len);
			mp->b_wptr += opt_len;
		}
		printd(("%s: %p: LMI_OPTMGMT_REQ ->\n", MTP_DRV_NAME, sl));
		putnext(sl->wq, mp);
		return (QR_ABSORBED);
	}
	ptrace(("%s: %p: ERROR: No buffers\n", MTP_DRV_NAME, sl));
	return (-ENOBUFS);
}

/*
 *  =========================================================================
 *
 *  MESSAGE ENCODING
 *
 *  =========================================================================
 */
typedef struct mtp_msg {
	queue_t *eq;				/* queue to write errors to */
	queue_t *xq;				/* queue to write results to */
	mtp_t *mtp;				/* MTP-User to which this message belongs */
	sl_t *sl;				/* Signalling Link to which thes message belongs */
	unsigned long timestamp;		/* jiffie clock timestamp */
	uint pvar;				/* protocol variant */
	uint popt;				/* protocol variant */
	uint mp;				/* message priority */
	uint ni;				/* network indicator */
	uint si;				/* service indicator */
	uint32_t opc;				/* originating point code */
	uint32_t dpc;				/* destination point code */
	uint sls;				/* signalling link selection */
	uint h0;				/* H0 value */
	uint h1;				/* H1 value */
	uint slc;				/* signalling link code */
	uint32_t dest;				/* destination point code */
	union {
		uint fsnl;			/* forward sequence number last acknowledged */
		uint cbc;			/* changeback code */
		uint stat;			/* status */
		uint sdli;			/* signalling data link identifier */
		uint upi;			/* user part identifier */
	} arg;
	caddr_t data;				/* user data */
	size_t dlen;				/* user data length */
} mtp_msg_t;
/*
 *  -------------------------------------------------------------------------
 *
 *  Encode message functions
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  Encode Changeover messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +-v-------------+
 *  |0|    FSNL     |
 *  +-^-------------+
 *  ANSI Format:
 *  +---------v-----+-------v-------+
 *  |    0    |    FSNL     |  SLC  |
 *  +---------^-----+-------^-------+
 */
static int mtp_enc_com(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		*mp->b_wptr++ = m->arg.fsnl & 0x7f;
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = (m->slc & 0x0f) | (m->arg.fsnl << 4);
		*mp->b_wptr++ = (m->arg.fsnl >> 4) & 0x7;
		break;
	}
	return (0);
}

/*
 *  Encode Changeback messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---------------+
 *  |      CBC      |
 *  +---------------+
 *  ANSI Format:
 *  +-------v-------+-------v-------+
 *  |   0   |      CBC      |  SLC  |
 *  +-------^-------+-------^-------+
 */
static int mtp_enc_cbm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		*mp->b_wptr++ = m->arg.cbc;
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = (m->slc & 0x0f) | (m->arg.cbc << 4);
		*mp->b_wptr++ = (m->arg.cbc >> 4) & 0x0f;
		break;
	}
	return (0);
}

/*
 *  Encode Link Management messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +
 *  | (SLC contained in SLS in RL)
 *  +
 *  ANSI Format:
 *  +-------v-------+
 *  |   0   |  SLC  |
 *  +-------^-------+
 */
static int mtp_enc_slm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = m->slc & 0x0f;
		break;
	}
	return (0);
}

/*
 *  Encode Trasfer Controlled messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---v-----------+---------------+
 *  | S |          DEST             |
 *  +---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format:
 *  +-----------v---+---------------+---------------+---------------+
 *  |     0     | S |                     DEST                      |
 *  +-----------^---+---------------+---------------+---------------+
 */
static int mtp_enc_tfc(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
	case SS7_PVAR_ITUT:
	case SS7_PVAR_ETSI:
		*mp->b_wptr++ = m->dest;
		*mp->b_wptr++ = ((m->dest >> 8) & 0x3f) | (m->arg.stat << 6);
		break;
	case SS7_PVAR_ANSI:
	case SS7_PVAR_JTTC:
	case SS7_PVAR_CHIN:
		*mp->b_wptr++ = m->dest;
		*mp->b_wptr++ = (m->dest >> 8);
		*mp->b_wptr++ = (m->dest >> 16);
		*mp->b_wptr++ = (m->arg.stat & 0x3);
		break;
	}
	return (0);
}

/*
 *  Encode Traffic Flow Control and Test messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---v-----------+---------------+
 *  | 0 |          DEST             |
 *  +---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format:
 *  +---------------+---------------+---------------+
 *  |                     DEST                      |
 *  +---------------+---------------+---------------+
 */
static int mtp_enc_tfm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
	case SS7_PVAR_ITUT:
	case SS7_PVAR_ETSI:
		*mp->b_wptr++ = m->dest;
		*mp->b_wptr++ = (m->dest >> 8) & 0x3f;
		break;
	case SS7_PVAR_ANSI:
	case SS7_PVAR_JTTC:
	case SS7_PVAR_CHIN:
		*mp->b_wptr++ = m->dest;
		*mp->b_wptr++ = (m->dest >> 8);
		*mp->b_wptr++ = (m->dest >> 16);
		break;
	}
	return (0);
}

/*
 *  Encode Data Link Connection message
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format: (12-bit SDLI)
 *  +-------v-------+---------------+
 *  |   0   |          SDLI         |
 *  +-------^-------+---------------+
 *  ANSI Format: (14-bit SDLI)
 *  +-----------v---+---------------+-------v-------+
 *  |     0     |           SDLI            |  SLC  |
 *  +-----------^---+---------------+-------^-------+
 */
static int mtp_enc_dlc(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		*mp->b_wptr++ = m->arg.sdli;
		*mp->b_wptr++ = (m->arg.sdli >> 8) & 0x0f;
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = (m->slc & 0x0f) | (m->arg.sdli << 4);
		*mp->b_wptr++ = m->arg.sdli >> 4;
		*mp->b_wptr++ = (m->arg.sdli >> 12) & 0x03;
		break;
	}
	return (0);
}

/*
 *  Encode User Part Flow Control messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +-------v-------+---v-----------+---------------+
 *  |   0   |  UPI  | 0 |          DEST             |
 *  +-------^-------+---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format:
 *  +-------v-------+---------------+---------------+---------------+
 *  |   0   |  UPI  |                     DEST                      |
 *  +-------^-------+---------------+---------------+---------------+
 *
 */
static int mtp_enc_upm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		*mp->b_wptr++ = m->dest;
		*mp->b_wptr++ = (m->dest >> 8) & 0x3f;
		*mp->b_wptr++ = m->arg.upi & 0x0f;
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = m->dest;
		*mp->b_wptr++ = m->dest >> 8;
		*mp->b_wptr++ = m->dest >> 16;
		*mp->b_wptr++ = m->arg.upi & 0x0f;
		break;
	}
	return (0);
}

/*
 *  Encode Signalling Link Test Messages/Acknowledgements
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  ------------------------+-------v-------+
 *   ...   Test Data        |  TLI  |   0   |
 *  ------------------------+-------^-------+
 *  ANSI Format:
 *  ------------------------+-------v-------+
 *   ...   Test Data        |  TLI  |  SLC  |
 *  ------------------------+-------^-------+
 */
static int mtp_enc_sltm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		*mp->b_wptr++ = m->dlen << 4;
		bcopy(mp->b_wptr, m->data, m->dlen);
		mp->b_wptr += m->dlen;
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = (m->slc & 0x0f) | (m->dlen << 4);
		bcopy(mp->b_wptr, m->data, m->dlen);
		mp->b_wptr += m->dlen;
		break;
	}
	return (0);
}

/*
 *  Encode User Part
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  All Formats:
 *  ------------------------+
 *   ...   User Part Data   |
 *  ------------------------+
 */
static int mtp_enc_user(mblk_t * mp, mtp_msg_t * m)
{
	bcopy(mp->b_wptr, m->data, m->dlen);
	mp->b_wptr += m->dlen;
	return (0);
}

/*
 *  Encode Service Information Octet
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---v---v-------+---v-----------+
 *  | NI| 0 |  SI   | 0 |           |
 *  +---^---^-------+---^-----------+
 *  ANSI Format:
 *  +---v---v-------+---v-----------+
 *  | NI| MP|  SI   | 0 |           |
 *  +---^---^-------+---^-----------+
 *  JTTC Format:
 *  +---v---v-------+---v-----------+
 *  | NI| 0 |  SI   | MP|           |
 *  +---^---^-------+---^-----------+
 */
static int mtp_enc_sio(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	case SS7_PVAR_JTTC:
		mp->b_wptr[-1] = m->mp << 6;	/* put message priority in header */
		*mp->b_wptr++ = (m->si & 0x0f) | ((m->ni & 0x3) << 6);
		break;
	case SS7_PVAR_ANSI:
		*mp->b_wptr++ = (m->si & 0x0f) | ((m->mp & 0x3) << 4) | ((m->ni & 0x3) << 6);
		break;
	default:
		if (m->popt & SS7_POPT_MPLEV)
			*mp->b_wptr++ = (m->si & 0x0f) | ((m->mp & 0x3) << 4) | ((m->ni & 0x3) << 6);
		else
			*mp->b_wptr++ = (m->si & 0x0f) | ((m->ni & 0x3) << 6);
		break;
	}
	return (0);
}

/*
 *  Encode Routing Label
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format: (14-bit PC, 4-bit SLS)
 *  +-------v-------+---------------+---v-----------+---------------+
 *  |  SLS  |            OPC            |            DPC            |
 *  +-------^-------+---------------+---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format: (24-bit PC, 5-bit SLS)
 *  +-----v---------+---------------+---------------+---------------+---------------+---------------+---------------+
 *  |     |   SLS   |                      OPC                      |                     DPC                       |
 *  +-----^---------+---------------+---------------+---------------+---------------+---------------+---------------+
 *  ANSI Format: (24-bit PC, 8-bit SLS)
 *  +---------------+---------------+---------------+---------------+---------------+---------------+---------------+
 *  |      SLS      |                      OPC                      |                     DPC                       |
 *  +---------------+---------------+---------------+---------------+---------------+---------------+---------------+
 *
 */
static int mtp_enc_rl(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
	case SS7_PVAR_ETSI:
	case SS7_PVAR_ITUT:
		*mp->b_wptr++ = m->dpc;
		*mp->b_wptr++ = ((m->dpc >> 8) & 0x3f) | (m->opc << 6);
		*mp->b_wptr++ = (m->opc >> 2);
		*mp->b_wptr++ = ((m->opc >> 10) & 0x0f) | (m->sls << 4);
		break;
	case SS7_PVAR_ANSI:
	case SS7_PVAR_JTTC:
	case SS7_PVAR_CHIN:
		*mp->b_wptr++ = m->dpc;
		*mp->b_wptr++ = m->dpc >> 8;
		*mp->b_wptr++ = m->dpc >> 16;
		*mp->b_wptr++ = m->opc;
		*mp->b_wptr++ = m->opc >> 8;
		*mp->b_wptr++ = m->opc >> 16;
		*mp->b_wptr++ = m->sls;
		break;
	}
	return (0);
}

/*
 *  Encode Service Information Field
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  All Formats:
 *  --------------------+-------v-------+
 *         signal       |   H1  |  H0   |
 *  --------------------+-------^-------+
 */
static int mtp_enc_sif(mblk_t * mp, mtp_msg_t * m)
{
	unsigned char tag;
	switch (m->si) {
	default:		/* USER */
		return mtp_enc_user(mp, m);
	case 0:		/* SNMM */
		tag = ((m->h0 & 0x0f) << 4) | (m->h1 & 0x0f);
		*mp->b_wptr++ = (m->h0 & 0x0f) | ((m->h1 & 0x0f) << 4);
		switch (tag) {
		case 0x11:	/* coo */
		case 0x12:	/* coa */
			return mtp_enc_com(mp, m);
		case 0x15:	/* cbd */
		case 0x16:	/* cba */
			return mtp_enc_cbm(mp, m);
		case 0x71:	/* tra */
		case 0x72:	/* trw */
		case 0x31:	/* rct */
			return (0);
		case 0x32:	/* tfc */
			return mtp_enc_tfc(mp, m);
		case 0x41:	/* tfp */
		case 0x42:	/* tcp */
		case 0x43:	/* tfr */
		case 0x44:	/* tcr */
		case 0x45:	/* tfa */
		case 0x46:	/* tca */
		case 0x51:	/* rst */
		case 0x52:	/* rsr */
		case 0x53:	/* rcp */
		case 0x54:	/* rcr */
			return mtp_enc_tfm(mp, m);
		case 0x81:	/* dlc */
			return mtp_enc_dlc(mp, m);
		case 0x82:	/* css */
		case 0x83:	/* cns */
		case 0x84:	/* cnp */
		case 0x21:	/* eco */
		case 0x22:	/* eca */
		case 0x61:	/* lin */
		case 0x62:	/* lun */
		case 0x63:	/* lia */
		case 0x64:	/* lua */
		case 0x65:	/* lid */
		case 0x66:	/* lfu */
		case 0x67:	/* llt */
		case 0x68:	/* lrt */
			return mtp_enc_slm(mp, m);
		case 0xa1:	/* upu */
		case 0xa2:	/* upa *//* ansi91 only */
		case 0xa3:	/* upt *//* ansi91 only */
			return mtp_enc_upm(mp, m);
		}
		break;
	case 1:		/* SNTM */
	case 2:		/* SNSM */
		tag = ((m->h0 & 0x0f) << 4) | (m->h1 & 0x0f);
		*mp->b_wptr++ = (m->h0 & 0x0f) | ((m->h1 & 0x0f) << 4);
		switch (tag) {
		case 0x11:	/* sltm */
		case 0x12:	/* slta */
			return mtp_enc_sltm(mp, m);
		}
		break;
	}
	swerr();
	return (-EFAULT);
}

#define max_msg_size (6 + 1 + 7 + 1 + 3 + 1)
static mblk_t *mtp_enc_msg(queue_t * q, mtp_msg_t * m, int *errp)
{
	mblk_t *mp;
	if ((mp = mtp_allocb(q, max_msg_size + m->dlen, BPRI_MED))) {
		mp->b_datap->db_type = M_DATA;
		bzero(mp->b_rptr, 6);	/* zero header */
		mp->b_rptr += 6;	/* reserve header room for Level 2 */
		mp->b_wptr = mp->b_rptr;
		if ((*errp = mtp_enc_sio(mp, m)))
			goto free_error;
		if ((*errp = mtp_enc_rl(mp, m)))
			goto free_error;
		if ((*errp = mtp_enc_sif(mp, m)))
			goto free_error;
		return (mp);
	}
	*errp = -ENOBUFS;
	return (NULL);
      free_error:
	freemsg(mp);
	swerr();
	return (NULL);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Build message functions
 *
 *  -------------------------------------------------------------------------
 *  These are partial build functions: they just "finish off" the pertinent
 *  message parameters.  All routing label items and arguments, and especially
 *  protocol variant and options, should be filled out by calling mtp_build_rl
 *  first.  After these function is called, mtp_enc_msg will generate the
 *  actual message.  The message can then be routed.
 */
static inline void mtp_build_rl(mtp_msg_t * m, uint pvar, uint ni, uint mp,
				uint si, uint32_t dpc, uint32_t opc, uint sls)
{
	m->pvar = pvar;
	m->ni = ni;
	m->mp = mp;
	m->si = si;
	m->dpc = dpc;
	m->opc = opc;
	m->sls = sls;
}
static inline void mtp_build_slc(mtp_msg_t * m, uint slc)
{
	if ((m->pvar & SS7_PVAR_MASK) != SS7_PVAR_ANSI)
		m->sls = slc;
	else
		m->slc = slc;
}
static inline mblk_t *mtp_build_coo(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc, uint fsnl)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 1;
	m->arg.fsnl = fsnl;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_coa(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc, uint fsnl)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 2;
	m->arg.fsnl = fsnl;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_cbd(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc, uint cbc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 5;
	m->arg.cbc = cbc;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_cba(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc, uint cbc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 6;
	m->arg.cbc = cbc;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_eco(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 2;
	m->h1 = 1;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_eca(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_slc(m, slc);
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 2;
	m->h1 = 2;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_rct(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint mp)
{
	mtp_build_rl(m, pvar, ni, mp, 0, dpc, opc, sls);
	m->h0 = 3;
	m->h1 = 1;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tfc(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest, uint stat)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 3;
	m->h1 = 2;
	m->dest = dest;
	m->arg.stat = stat;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tfp(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 4;
	m->h1 = 1;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tcp(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 4;
	m->h1 = 2;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tfr(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 4;
	m->h1 = 3;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tcr(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 4;
	m->h1 = 4;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tfa(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 4;
	m->h1 = 5;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tca(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 4;
	m->h1 = 6;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_rst(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 5;
	m->h1 = 1;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_rsr(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 5;
	m->h1 = 2;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_rcp(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 5;
	m->h1 = 3;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_rcr(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 5;
	m->h1 = 4;
	m->dest = dest;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lin(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 1;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lun(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 2;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lia(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 3;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lua(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 4;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lid(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 5;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lfu(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 6;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_llt(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 7;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_lrt(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 6;
	m->h1 = 8;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_tra(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 7;
	m->h1 = 1;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_trw(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 7;
	m->h1 = 2;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_dlc(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc, uint sdli)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 8;
	m->h1 = 1;
	m->arg.sdli = sdli;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_css(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 8;
	m->h1 = 2;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_cns(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 8;
	m->h1 = 3;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_cnp(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 8;
	m->h1 = 4;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_upu(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest, uint upi)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 10;
	m->h1 = 1;
	m->dest = dest;
	m->arg.upi = upi;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_upa(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest, uint upi)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 10;
	m->h1 = 2;
	m->dest = dest;
	m->arg.upi = upi;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_upt(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				    uint32_t dpc, uint32_t opc, uint sls, uint32_t dest, uint upi)
{
	mtp_build_rl(m, pvar, ni, 3, 0, dpc, opc, sls);
	m->h0 = 10;
	m->h1 = 3;
	m->dest = dest;
	m->arg.upi = upi;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_sltm(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				     uint32_t dpc, uint32_t opc, uint sls, uint slc,
				     unsigned char *data, size_t dlen)
{
	mtp_build_rl(m, pvar, ni, 3, 1, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 1;
	m->data = data;
	m->dlen = dlen;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_slta(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				     uint32_t dpc, uint32_t opc, uint sls, uint slc,
				     unsigned char *data, size_t dlen)
{
	mtp_build_rl(m, pvar, ni, 3, 1, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 2;
	m->data = data;
	m->dlen = dlen;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_ssltm(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				      uint32_t dpc, uint32_t opc, uint sls, uint slc,
				      unsigned char *data, size_t dlen)
{
	mtp_build_rl(m, pvar, ni, 3, 2, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 1;
	m->data = data;
	m->dlen = dlen;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_sslta(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				      uint32_t dpc, uint32_t opc, uint sls, uint slc,
				      unsigned char *data, size_t dlen)
{
	mtp_build_rl(m, pvar, ni, 3, 2, dpc, opc, sls);
	mtp_build_slc(m, slc);
	m->h0 = 1;
	m->h1 = 2;
	m->data = data;
	m->dlen = dlen;
	return mtp_enc_msg(q, m, errp);
}
static inline mblk_t *mtp_build_user(queue_t * q, mtp_msg_t * m, int *errp, uint pvar, uint ni,
				     uint32_t dpc, uint32_t opc, uint sls, uint mp, uint si,
				     unsigned char *data, size_t dlen)
{
	mtp_build_rl(m, pvar, ni, mp, si, dpc, opc, sls);
	m->data = data;
	m->dlen = dlen;
	return mtp_enc_msg(q, m, errp);
}

/*
 *  Send message functions
 *  -------------------------------------------------------------------------
 */
static inline int mtp_send_route(queue_t * q, mtp_msg_t * m, mblk_t * mp)
{
	sl_t *sl;
	for (sl = sl_list; sl; sl = sl->next)
		if (sl->adj == m->dpc)
			break;
	if (sl) {
		if (canputnext(sl->wq)) {
			putnext(sl->wq, mp);
			return (QR_ABSORBED);
		}
		return (-EBUSY);
	}
	__ptrace(("%s: %p: Discarding message for DPC = %x\n", MTP_DRV_NAME, sl, m->dpc));
	return (-EPROTO);
}
static int mtp_send_coo(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint slc, uint fsnl)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_coo(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, fsnl))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_coa(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint slc, uint fsnl)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_coa(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, fsnl))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_cbd(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc, uint cbc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_cbd(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, cbc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_cba(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc, uint cbc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_cba(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, cbc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_eco(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_eco(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_eca(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_eca(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_rct(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint mp)
{
	mblk_t *bp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((bp = mtp_build_rct(q, &msg, &err, pvar, ni, dpc, opc, sls, mp))) {
		if ((err = mtp_send_route(q, &msg, bp)) != QR_ABSORBED)
			freemsg(bp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tfc(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint32_t dest, uint stat)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tfc(q, &msg, &err, pvar, ni, dpc, opc, sls, dest, stat))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tfp(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tfp(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tcp(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tcp(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tfr(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tfr(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tcr(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tcr(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tfa(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tfa(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tca(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tca(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_rst(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_rst(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_rsr(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_rsr(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_rcp(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_rcp(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_rcr(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint32_t dest)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_rcr(q, &msg, &err, pvar, ni, dpc, opc, sls, dest))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lin(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lin(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lun(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lun(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lia(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lia(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lua(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lua(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lid(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lid(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lfu(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lfu(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_llt(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_llt(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_lrt(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_lrt(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_tra(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_tra(q, &msg, &err, pvar, ni, dpc, opc, sls))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_trw(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_trw(q, &msg, &err, pvar, ni, dpc, opc, sls))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_dlc(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint slc, uint sdli)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_dlc(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, sdli))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_css(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_css(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_cns(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_cns(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_cnp(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls, uint slc)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_cnp(q, &msg, &err, pvar, ni, dpc, opc, sls, slc))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_upu(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint32_t dest, uint upi)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_upu(q, &msg, &err, pvar, ni, dpc, opc, sls, dest, upi))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_upa(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint32_t dest, uint upi)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_upa(q, &msg, &err, pvar, ni, dpc, opc, sls, dest, upi))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_upt(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			uint32_t dest, uint upi)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_upt(q, &msg, &err, pvar, ni, dpc, opc, sls, dest, upi))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_sltm(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			 uint slc, unsigned char *data, size_t dlen)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_sltm(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, data, dlen))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_slta(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			 uint slc, unsigned char *data, size_t dlen)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_slta(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, data, dlen))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_ssltm(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			  uint slc, unsigned char *data, size_t dlen)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_ssltm(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, data, dlen))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_sslta(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			  uint slc, unsigned char *data, size_t dlen)
{
	mblk_t *mp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((mp = mtp_build_sslta(q, &msg, &err, pvar, ni, dpc, opc, sls, slc, data, dlen))) {
		if ((err = mtp_send_route(q, &msg, mp)) != QR_ABSORBED)
			freemsg(mp);
		return (QR_DONE);
	}
	return (err);
}
static int mtp_send_user(queue_t * q, uint pvar, uint ni, uint32_t dpc, uint32_t opc, uint sls,
			 uint mp, uint si, unsigned char *data, size_t dlen)
{
	mblk_t *bp;
	int err = 0;
	mtp_msg_t msg = { 0, };
	if ((bp = mtp_build_user(q, &msg, &err, pvar, ni, dpc, opc, sls, mp, si, data, dlen))) {
		if ((err = mtp_send_route(q, &msg, bp)) != QR_ABSORBED)
			freemsg(bp);
		return (QR_DONE);
	}
	return (err);
}

/*
 *  =========================================================================
 *
 *  STATE MACHINES
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  MTP User procedures
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  MTP-STATUS-IND
 *  -----------------------------------
 *  Indicate to an MTP User an MTP-STATUS-IND with respect to the concerned
 *  routesets.
 */
static inline int mtp_status_ind(queue_t * q, mtp_t * mtp, mtp_addr_t * dst, int status)
{
	return t_uderror_ind(q, mtp, dst, NULL, NULL, status);
}
static int mtp_up_status_ind(queue_t * q, mtp_t * mtp, uint32_t dest, uint user, uint status)
{
	mtp_addr_t dst = mtp->src;
	uint error;
	dst.mtp_pc = dest;
	dst.mtp_si = user;
	return mtp_status_ind(q, mtp, &dst, error);
}
static int mtp_up_status_ind_all_local(queue_t * q, uint32_t dest, uint si, uint status)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mtp_t *mtp;
	if ((mtp = sl->users[si & 0xf]))
		if ((err = mtp_up_status_ind(q, mtp, dest, si, status)))
			return (err);
	return (0);
}
static int mtp_cong_status_ind(queue_t * q, mtp_t * mtp, uint32_t dest, uint status)
{
	mtp_addr_t dst = mtp->src;
	uint error = T_MTP_CONGESTED(status) | T_MTP_M_MEMBER;
	dst.mtp_pc = dest;
	return mtp_status_ind(q, mtp, &dst, error);
}
static int mtp_cong_status_ind_all_local(queue_t * q, uint32_t dest, uint status)
{
	sl_t *sl = SL_PRIV(q);
	int i, err;
	mtp_t *mtp;
	for (i = 0; i < 16; i++)
		if ((mtp = sl->users[i]))
			if ((err = mtp_cong_status_ind(q, mtp, dest, status)))
				return (err);
	return (0);
}

/*
 *  MTP-PAUSE-IND
 *  -----------------------------------
 */
static int mtp_pause_ind(queue_t * q, mtp_t * mtp, uint32_t dest)
{
	mtp_addr_t dst = mtp->src;
	uint error = T_MTP_PROHIBITED | T_MTP_M_MEMBER;
	dst.mtp_pc = dest;
	return mtp_status_ind(q, mtp, &dst, error);
}
static int mtp_pause_ind_all_local(queue_t * q, uint32_t dest)
{
	sl_t *sl = SL_PRIV(q);
	int i, err;
	mtp_t *mtp;
	for (i = 0; i < 16; i++)
		if ((mtp = sl->users[i]))
			if ((err = mtp_pause_ind(q, mtp, dest)))
				return (err);
	return (0);
}

/*
 *  MTP-RESUME-IND
 *  -----------------------------------
 */
static int mtp_resume_ind(queue_t * q, mtp_t * mtp, uint32_t dest)
{
	mtp_addr_t dst = mtp->src;
	uint error = T_MTP_AVAILABLE | T_MTP_M_MEMBER;
	dst.mtp_pc = dest;
	return mtp_status_ind(q, mtp, &dst, error);
}
static int mtp_resume_ind_all_local(queue_t * q, uint32_t dest)
{
	sl_t *sl = SL_PRIV(q);
	int i, err;
	mtp_t *mtp;
	for (i = 0; i < 16; i++)
		if ((mtp = sl->users[i]))
			if ((err = mtp_resume_ind(q, mtp, dest)))
				return (err);
	return (0);
}

/*
 *  MTP-TRANSFER-IND
 *  -----------------------------------
 */
static int mtp_transfer_ind(queue_t * q, mtp_t * mtp, mtp_msg_t * m)
{
	if (mtp) {
	}
	return (QR_DONE);
}
static int mtp_transfer_ind_local(queue_t * q, mtp_msg_t * m)
{
	sl_t *sl = SL_PRIV(q);
	mtp_t *mtp;
	if ((mtp = sl->users[m->si & 0x0f]))
		return mtp_transfer_ind(q, mtp, m);
	/* FIXME: should send unqeuipped user */
	return (QR_DONE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Timers
 *
 *  -------------------------------------------------------------------------
 */
enum {
	tcon,
	t1,
	t2,
	t3,
	t4,
	t5,
	t6,
	t7,
	t8,
	t9,					/* not used */
	t10,
	t11,
	t12,
	t13,
	t14,
	t15,
	t16,
	t17,
	t18,
	t19,
	t20,
	t21,
	t22,
	t23,
	t24,
	t1t,
	t2t,
	t1s
};
STATIC INLINE mblk_t *mtp_alloc_expiry(ulong timer, caddr_t data)
{
	mblk_t *mp;
	if ((mp = allocb(sizeof(ulong), BPRI_HI))) {
		mp->b_datap->db_type = M_RSE;
		// mp->b_datap->db_type = M_PCRSE;
		*((ulong *) mp->b_wptr)++ = timer;
		*((caddr_t *) mp->b_wptr)++ = data;
	}
	return (mp);
}

/*
 *  MTP timers
 *  -----------------------------------
 */
STATIC void sl_t1t_expiry(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (xchg(&sl->timers.t1t, 0)) {
		mblk_t *mp;
		if ((mp = mtp_alloc_expiry(t1t, data))) {
			printd(("%s: %p: t1t timeout at %lu\n", MTP_DRV_NAME, sl, jiffies));
			sl->refcnt--;
			putq(sl->rq, mp);
		} else {
			printd(("%s: %p: t1t timeout collision\n", MTP_DRV_NAME, sl));
			/* back off timer 100 ms */
			sl->timers.t1t = timeout(&sl_t1t_expiry, (caddr_t) sl, 10);
		}
	}
	return;
}
STATIC void sl_t2t_expiry(caddr_t data)
{
	sl_t *sl = (sl_t *) data;
	if (xchg(&sl->timers.t2t, 0)) {
		mblk_t *mp;
		if ((mp = mtp_alloc_expiry(t2t, data))) {
			printd(("%s: %p: t2t timeout at %lu\n", MTP_DRV_NAME, sl, jiffies));
			sl->refcnt--;
			putq(sl->rq, mp);
		} else {
			printd(("%s: %p: t2t timeout collision\n", MTP_DRV_NAME, sl));
			/* back off timer 100 ms */
			sl->timers.t2t = timeout(&sl_t2t_expiry, (caddr_t) sl, 10);
		}
	}
	return;
}
STATIC inline void sl_timer_stop(sl_t * sl, const uint t)
{
	switch (t) {
	case t1t:
		if (sl->timers.t1t) {
			printd(("%s: %p: stopping t1t at %lu\n", MTP_DRV_NAME, sl, jiffies));
			untimeout(xchg(&sl->timers.t1t, 0));
			sl->refcnt--;
		}
		break;
	case t2t:
		if (sl->timers.t2t) {
			printd(("%s: %p: stopping t2t at %lu\n", MTP_DRV_NAME, sl, jiffies));
			untimeout(xchg(&sl->timers.t2t, 0));
			sl->refcnt--;
		}
		break;
	default:
		swerr();
		break;
	}
}
STATIC inline void sl_timer_start(sl_t * sl, const uint t)
{
	int flags;
	lis_spin_lock_irqsave(&sl->qlock, &flags);
	{
		sl_timer_stop(sl, t);
		switch (t) {
		case t1t:
			printd(("%s: %p: starting t1t %lu ms at %lu\n", MTP_DRV_NAME, sl,
				sl->config.t1t * 10, jiffies));
			sl->timers.t1t = timeout(&sl_t1t_expiry, (caddr_t) sl, sl->config.t1t);
			sl->refcnt++;
			break;
		case t2t:
			printd(("%s: %p: starting t2t %lu ms at %lu\n", MTP_DRV_NAME, sl,
				sl->config.t2t * 10, jiffies));
			sl->timers.t2t = timeout(&sl_t2t_expiry, (caddr_t) sl, sl->config.t2t);
			sl->refcnt++;
			break;
		default:
			swerr();
			break;
		}
	}
	lis_spin_unlock_irqrestore(&sl->qlock, &flags);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Timeout functions
 *
 *  -------------------------------------------------------------------------
 */

/*
 *  -------------------------------------------------------------------------
 *
 *  Receive message functions
 *
 *  -------------------------------------------------------------------------
 */
static int mtp_recv_coo(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("COO Unimplemented\n"));
	return (-EFAULT);
}
static int mtp_recv_coa(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("COA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_cbd(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("CBD Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_cba(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("CBA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_eco(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("ECO Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_eca(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("ECA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_rct(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	/* 13.9.6.  When a signalling route set congestino test message reaches its destination, it is
	   discarded. */
	return (QR_DONE);
}
static int mtp_recv_tfc(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TFC Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tfp(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TFP Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tcp(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TCP Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tfr(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TFR Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tcr(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TCR Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tfa(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TFA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tca(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TCA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_rst(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("RST Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_rsr(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("RSR Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_rcp(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("RCP Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_rcr(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("RCR Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lin(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LIN Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lun(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LUN Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lia(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LIA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lua(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LUA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lid(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LID Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lfu(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LFU Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_llt(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LLT Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_lrt(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("LRT Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_tra(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TRA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_trw(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("TRW Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_dlc(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("DLC Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_css(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("CSS Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_cns(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("CNS Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_cnp(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("CNP Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_upu(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	fixme(("Handle UPU\n"));
	return (-EFAULT);
}
static int mtp_recv_upa(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("UPA Unimplemeneted\n"));
	return (-EFAULT);
}
static int mtp_recv_upt(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("UPT Unimplemeneted\n"));
	return (-EFAULT);
}

#define SLS_OUT_OF_SERVICE	    0
//#define SLS_IN_SERVICE		    1
#define SLS_PENDING_ACTIVATION	    2
#define SLS_PENDING_DEACTIVATION    3

#define SL_F_WACK_SLTM	0x00000001
#define SL_F_WACK_SLTM2	0x00000002

/*
 *  RECV SLTM
 *  -----------------------------------
 */
static int mtp_recv_sltm(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	sl_t *sl = SL_PRIV(q);
	if (m->slc != sl->slc || m->opc != sl->adj || m->dpc != sl->loc)
		goto eproto;
	return mtp_send_slta(q, SS7_PVAR_ETSI_00, 0, m->opc, m->dpc, m->sls, sl->slc, m->data, m->dlen);
      eproto:
	return (-EPROTO);
}

/*
 *  TIMER T1T  -  SLTM ack
 *  -----------------------------------
 */
STATIC inline int sl_t1t_timeout(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	if (sl->flags & (SL_F_WACK_SLTM | SL_F_WACK_SLTM2)) {
		if (sl->flags & SL_F_WACK_SLTM) {
			mtp_send_sltm(q, SS7_PVAR_ETSI_00, 0, sl->adj, sl->loc, 0, sl->slc, sl->tdata, sl->tlen);
			sl_timer_start(sl, t1t);
			sl->flags &= ~SL_F_WACK_SLTM;
			sl->flags |= SL_F_WACK_SLTM2;
			return (0);
		}
		sl_sltm_failed(sl->rq, sl);
		sl->flags &= ~SL_F_WACK_SLTM2;
		return (0);
	}
	swerr();
	return (-EFAULT);
}

/*
 *  TIMER T2T  -  SLTM interval
 *  -----------------------------------
 */
STATIC inline int sl_t2t_timeout(queue_t * q)
{
	sl_t *sl = SL_PRIV(q);
	if (sl->state == SLS_IN_SERVICE) {
		int i;
		unsigned long random = jiffies;
		sl->flags &= ~SL_F_WACK_SLTM2;
		sl->flags |= SL_F_WACK_SLTM;
		/* generate new test pattern */
		sl->tlen = jiffies & 0xf;
		for (i = 0; i < 4; i++)
			bcopy(&random, sl->tdata + (i << 2), 4);
		mtp_send_sltm(q, SS7_PVAR_ETSI_00, 0, sl->adj, sl->loc, 0, sl->slc, sl->tdata, sl->tlen);
		sl_timer_start(sl, t1t);
		return (0);
	}
	swerr();
	return (-EFAULT);
}

/*
 *  RECV SLTA
 *  -----------------------------------
 */
static int mtp_recv_slta(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	sl_t *sl = SL_PRIV(q);
	int err, i;
	if (sl->state != SLS_IN_SERVICE)
		goto eproto;
	if (!(sl->flags & (SL_F_WACK_SLTM | SL_F_WACK_SLTM2)))
		goto eproto;
	sl_timer_stop(sl, t1t);
	if (m->slc != sl->slc || m->opc != sl->adj || m->dpc != sl->loc)
		goto failed;
	if (sl->tlen != m->dlen)
		goto failed;
	for (i = 0; i < m->dlen; i++)
		if (m->data[i] != sl->tdata[i])
			goto failed;
      wait_next:
	sl_timer_start(sl, t2t);
	return (QR_DONE);
      failed:
	if (sl->flags & SL_F_WACK_SLTM) {
		if ((err = mtp_send_sltm(q, SS7_PVAR_ETSI_00, 0, sl->adj, sl->loc, 0, sl->slc,
					 sl->tdata, sl->tlen)))
			return (err);
		sl_timer_start(sl, t1t);
		sl->flags &= ~SL_F_WACK_SLTM;
		sl->flags |= ~SL_F_WACK_SLTM2;
		return (QR_DONE);
	}
	sl->flags &= ~SL_F_WACK_SLTM2;
	fixme(("Implement link failure function\n"));
	goto wait_next;
      eproto:
	__ptrace(("%s: %p: Unexpected SLTA\n", MTP_DRV_NAME, sl));
	return (-EPROTO);
}

/*
 *  RECV SSLTM
 *  -----------------------------------
 */
static int mtp_recv_ssltm(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	fixme(("Handle SSLTM\n"));
	return (-EFAULT);
}

/*
 *  RECV SSLTA
 *  -----------------------------------
 */
static int mtp_recv_sslta(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	__ptrace(("SSLTA Unimplemeneted\n"));
	return (-EFAULT);
}

/*
 *  RECV USER
 *  -----------------------------------
 */
static int mtp_recv_user(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	sl_t *sl = SL_PRIV(q);
	mtp_t *mtp;
	if ((mtp = sl->users[m->si])) {
		if (canput(mtp->rq)) {
			putq(mtp->rq, mp);
			return (QR_ABSORBED);
		}
		return (-EBUSY);
	}
	return sl_reply_upu(q, mp, m);
}

/*
 *  =========================================================================
 *
 *  MESSAGE DECODING
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  Process message functions
 *
 *  -------------------------------------------------------------------------
 */
static int mtp_proc_msg(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	unsigned char tag;
	switch (m->si) {
	default:		/* USER PART */
		return mtp_recv_user(q, mp, m);
	case 0:		/* SNMM */
		tag = ((m->h0 & 0x0f) << 4) | (m->h1 & 0x0f);
		switch (tag) {
		case 0x11:	/* coo */
			return mtp_recv_coo(q, mp, m);
		case 0x12:	/* coa */
			return mtp_recv_coa(q, mp, m);
		case 0x15:	/* cbd */
			return mtp_recv_cbd(q, mp, m);
		case 0x16:	/* cba */
			return mtp_recv_cba(q, mp, m);
		case 0x21:	/* eco */
			return mtp_recv_eco(q, mp, m);
		case 0x22:	/* eca */
			return mtp_recv_eca(q, mp, m);
		case 0x31:	/* rct */
			return mtp_recv_rct(q, mp, m);
		case 0x32:	/* tfc */
			return mtp_recv_tfc(q, mp, m);
		case 0x41:	/* tfp */
			return mtp_recv_tfp(q, mp, m);
		case 0x42:	/* tcp */
			return mtp_recv_tcp(q, mp, m);
		case 0x43:	/* tfr */
			return mtp_recv_tfr(q, mp, m);
		case 0x44:	/* tcr */
			return mtp_recv_tcr(q, mp, m);
		case 0x45:	/* tfa */
			return mtp_recv_tfa(q, mp, m);
		case 0x46:	/* tca */
			return mtp_recv_tca(q, mp, m);
		case 0x51:	/* rst */
			return mtp_recv_rst(q, mp, m);
		case 0x52:	/* rsr */
			return mtp_recv_rsr(q, mp, m);
		case 0x53:	/* rcp */
			return mtp_recv_rcp(q, mp, m);
		case 0x54:	/* rcr */
			return mtp_recv_rcr(q, mp, m);
		case 0x61:	/* lin */
			return mtp_recv_lin(q, mp, m);
		case 0x62:	/* lun */
			return mtp_recv_lun(q, mp, m);
		case 0x63:	/* lia */
			return mtp_recv_lia(q, mp, m);
		case 0x64:	/* lua */
			return mtp_recv_lua(q, mp, m);
		case 0x65:	/* lid */
			return mtp_recv_lid(q, mp, m);
		case 0x66:	/* lfu */
			return mtp_recv_lfu(q, mp, m);
		case 0x67:	/* llt */
			return mtp_recv_llt(q, mp, m);
		case 0x68:	/* lrt */
			return mtp_recv_lrt(q, mp, m);
		case 0x71:	/* tra */
			return mtp_recv_tra(q, mp, m);
		case 0x72:	/* trw */
			return mtp_recv_trw(q, mp, m);
		case 0x81:	/* dlc */
			return mtp_recv_dlc(q, mp, m);
		case 0x82:	/* css */
			return mtp_recv_css(q, mp, m);
		case 0x83:	/* cns */
			return mtp_recv_cns(q, mp, m);
		case 0x84:	/* cnp */
			return mtp_recv_cnp(q, mp, m);
		case 0xa1:	/* upu */
			return mtp_recv_upu(q, mp, m);
		case 0xa2:	/* upa *//* ansi91 only */
			return mtp_recv_upa(q, mp, m);
		case 0xa3:	/* upt *//* ansi91 only */
			return mtp_recv_upt(q, mp, m);
		}
		break;
	case 1:		/* SNTM */
		tag = ((m->h0 & 0x0f) << 4) | (m->h1 & 0x0f);
		switch (tag) {
		case 0x11:	/* sltm */
			return mtp_recv_sltm(q, mp, m);
		case 0x12:	/* slta */
			return mtp_recv_slta(q, mp, m);
		}
		break;
	case 2:		/* SSNTM */
		tag = ((m->h0 & 0x0f) << 4) | (m->h1 & 0x0f);
		switch (tag) {
		case 0x11:	/* ssltm */
			return mtp_recv_ssltm(q, mp, m);
		case 0x12:	/* sslta */
			return mtp_recv_sslta(q, mp, m);
		}
		break;
	}
	swerr();
	return (-EPROTO);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Decode message functions
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  Decode Changeover messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +-v-------------+
 *  |0|    FSNL     |
 *  +-^-------------+
 *  ANSI Format:
 *  +---------v-----+-------v-------+
 *  |    0    |    FSNL     |  SLC  |
 *  +---------^-----+-------^-------+
 */
static int mtp_dec_com(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->slc = m->sls;
		m->arg.fsnl = *mp->b_rptr++ & 0x7f;
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 2)
			break;
		m->slc = *mp->b_rptr & 0x0f;
		m->arg.fsnl = (*mp->b_rptr++ >> 4) | ((*mp->b_rptr++ & 0x7) << 4);
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Changeback messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---------------+
 *  |      CBC      |
 *  +---------------+
 *  ANSI Format:
 *  +-------v-------+-------v-------+
 *  |   0   |      CBC      |  SLC  |
 *  +-------^-------+-------^-------+
 */
static int mtp_dec_cbm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->slc = m->sls;
		m->arg.cbc = *mp->b_rptr++;
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 2)
			break;
		m->slc = *mp->b_rptr & 0x0f;
		m->arg.cbc = (*mp->b_rptr++ >> 4) | ((*mp->b_rptr++ & 0xf) << 4);
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Link Management messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +
 *  | (SLC contained in SLS in RL)
 *  +
 *  ANSI Format:
 *  +-------v-------+
 *  |   0   |  SLC  |
 *  +-------^-------+
 */
static int mtp_dec_slm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		m->slc = m->sls;
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->slc = *mp->b_rptr++ & 0x0f;
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Transfer Controlled messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---v-----------+---------------+
 *  | S |          DEST             |
 *  +---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format:
 *  +-----------v---+---------------+---------------+---------------+
 *  |     0     | S |                     DEST                      |
 *  +-----------^---+---------------+---------------+---------------+
 */
static int mtp_dec_tfc(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
	case SS7_PVAR_ITUT:
	case SS7_PVAR_ETSI:
		if (mp->b_wptr < mp->b_rptr + 2)
			break;
		m->dest = (*mp->b_rptr++) | ((*mp->b_rptr & 0x3f) << 8);
		m->arg.stat = *mp->b_rptr++ >> 6;
		return (0);
	case SS7_PVAR_ANSI:
	case SS7_PVAR_JTTC:
	case SS7_PVAR_CHIN:
		if (mp->b_wptr < mp->b_rptr + 4)
			break;
		m->dest = (*mp->b_rptr++) | (*mp->b_rptr++ << 8) | (*mp->b_rptr++ << 16);
		m->arg.stat = (*mp->b_rptr++ & 0x3);
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Traffic Flow Control and Test messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---v-----------+---------------+
 *  | 0 |          DEST             |
 *  +---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format:
 *  +---------------+---------------+---------------+
 *  |                     DEST                      |
 *  +---------------+---------------+---------------+
 */
static int mtp_dec_tfm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
	case SS7_PVAR_ITUT:
	case SS7_PVAR_ETSI:
		if (mp->b_wptr < mp->b_rptr + 2)
			break;
		m->dest = (*mp->b_rptr++) | ((*mp->b_rptr & 0x3f) << 8);
		return (0);
	case SS7_PVAR_ANSI:
	case SS7_PVAR_JTTC:
	case SS7_PVAR_CHIN:
		if (mp->b_wptr < mp->b_rptr + 3)
			break;
		m->dest = (*mp->b_rptr++) | (*mp->b_rptr++ << 8) | (*mp->b_rptr++ << 16);
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Data Link Connection message
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format: (12-bit SDLI)
 *  +-------v-------+---------------+
 *  |   0   |          SDLI         |
 *  +-------^-------+---------------+
 *  ANSI Format: (14-bit SDLI)
 *  +-----------v---+---------------+-------v-------+
 *  |     0     |           SDLI            |  SLC  |
 *  +-----------^---+---------------+-------^-------+
 */
static int mtp_dec_dlc(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		if (mp->b_wptr < mp->b_rptr + 2)
			break;
		m->slc = m->sls;
		m->arg.sdli = (*mp->b_rptr++) | ((*mp->b_rptr++ & 0x0f) << 8);
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 3)
			break;
		m->slc = (*mp->b_rptr & 0x0f);
		m->arg.sdli = (*mp->b_rptr++ >> 4) | (*mp->b_rptr++ << 4) | ((*mp->b_rptr++ & 0x03) << 12);
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode User Part Flow Control messages
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +-------v-------+---v-----------+---------------+
 *  |   0   |  UPI  | 0 |          DEST             |
 *  +-------^-------+---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format:
 *  +-------v-------+---------------+---------------+---------------+
 *  |   0   |  UPI  |                     DEST                      |
 *  +-------^-------+---------------+---------------+---------------+
 *
 */
static int mtp_dec_upm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		if (mp->b_wptr < mp->b_rptr + 3)
			break;
		m->dest = ((*mp->b_rptr++)) | ((*mp->b_rptr++) & 0x3f << 8);
		m->arg.upi = (*mp->b_rptr++ & 0x0f);
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 4)
			break;
		m->dest = ((*mp->b_rptr++)) | ((*mp->b_rptr++ << 8)) | ((*mp->b_rptr++ << 16));
		m->arg.upi = (*mp->b_rptr++ & 0x0f);
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Signalling Link Test Messages/Acknowledgements
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  ------------------------+-------v-------+
 *   ...   Test Data        |  TLI  |   0   |
 *  ------------------------+-------^-------+
 *  ANSI Format:
 *  ------------------------+-------v-------+
 *   ...   Test Data        |  TLI  |  SLC  |
 *  ------------------------+-------^-------+
 */
static int mtp_dec_sltm(mblk_t * mp, mtp_msg_t * m)
{
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
		if (m->si == 2)
			break;
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->slc = m->sls;
		m->dlen = *mp->b_rptr++ >> 4;
		m->data = mp->b_rptr;
		if (mp->b_rptr + m->dlen > mp->b_wptr)
			break;
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->slc = *mp->b_rptr & 0x0f;
		m->dlen = *mp->b_rptr++ >> 4;
		m->data = mp->b_rptr;
		if (mp->b_wptr < mp->b_rptr + m->dlen)
			break;
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode User Part
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  All Formats:
 *  ------------------------+
 *   ...   User Part Data   |
 *  ------------------------+
 */
static int mtp_dec_user(mblk_t * mp, mtp_msg_t * m)
{
	if (mp->b_wptr < mp->b_rptr)
		goto error;
	m->data = mp->b_rptr;
	m->dlen = mp->b_wptr - mp->b_rptr;
	return (0);
      error:
	return (-EPROTO);
}

/*
 *  Decode Service Information Octet
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format:
 *  +---v---v-------+---v-----------+
 *  | NI| 0 |  SI   | 0 |           |
 *  +---^---^-------+---^-----------+
 *  ANSI Format:
 *  +---v---v-------+---v-----------+
 *  | NI| MP|  SI   | 0 |           |
 *  +---^---^-------+---^-----------+
 *  JTTC Format:
 *  +---v---v-------+---v-----------+
 *  | NI| 0 |  SI   | MP|           |
 *  +---^---^-------+---^-----------+
 */
static int mtp_dec_sio(mblk_t * mp, mtp_msg_t * m)
{
	/* decode si, mp and ni */
	switch (m->pvar & SS7_PVAR_MASK) {
	case SS7_PVAR_JTTC:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->si = *mp->b_rptr & 0x0f;
		m->mp = (mp->b_rptr[-1] >> 6);	/* get mp from header */
		m->ni = (*mp->b_rptr++ >> 6) & 0x3;
		return (0);
	case SS7_PVAR_ANSI:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->si = *mp->b_rptr & 0x0f;
		m->mp = (*mp->b_rptr >> 4) & 0x3;
		m->ni = (*mp->b_rptr++ >> 6) & 0x3;
		return (0);
	default:
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		m->si = *mp->b_rptr & 0x0f;
		if (m->popt & SS7_POPT_MPLEV)
			m->mp = (*mp->b_rptr >> 4) & 0x3;
		else
			m->mp = 0;
		m->ni = (*mp->b_rptr++ >> 6) & 0x3;
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Routing Label
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  ITUT, ETSI Format: (14-bit PC, 4-bit SLS)
 *  +-------v-------+---------------+---v-----------+---------------+
 *  |  SLS  |            OPC            |            DPC            |
 *  +-------^-------+---------------+---^-----------+---------------+
 *  ANSI, JTTC, CHIN Format: (24-bit PC, 5-bit SLS)
 *  +-----v---------+---------------+---------------+---------------+---------------+---------------+---------------+
 *  |     |   SLS   |                      OPC                      |                     DPC                       |
 *  +-----^---------+---------------+---------------+---------------+---------------+---------------+---------------+
 *  ANSI Format: (24-bit PC, 8-bit SLS)
 *  +---------------+---------------+---------------+---------------+---------------+---------------+---------------+
 *  |      SLS      |                      OPC                      |                     DPC                       |
 *  +---------------+---------------+---------------+---------------+---------------+---------------+---------------+
 */
static int mtp_dec_rl(mblk_t * mp, mtp_msg_t * m)
{
	/* decode the routing label */
	switch (m->pvar & SS7_PVAR_MASK) {
	default:
	case SS7_PVAR_ETSI:
	case SS7_PVAR_ITUT:
		/* 14-bit point codes - 32-bit RL */
		if (mp->b_wptr < mp->b_rptr + 4)
			break;
		m->dpc = (*mp->b_rptr++ | ((*mp->b_rptr & 0x3f) << 8));
		m->opc = (((*mp->b_rptr++ >> 6) & 0x3) | (*mp->b_rptr++ << 2) | ((*mp->b_rptr & 0x0f) << 10));
		m->sls = (*mp->b_rptr++ >> 4) & 0x0f;
		return (0);
	case SS7_PVAR_ANSI:
	case SS7_PVAR_JTTC:
	case SS7_PVAR_CHIN:
		/* 24-bit point codes - 56-bit RL */
		if (mp->b_wptr < mp->b_rptr + 7)
			break;
		m->dpc = ((*mp->b_rptr++ | (*mp->b_rptr++ << 8) | (*mp->b_rptr++ << 16))) & 0x00ffffff;
		m->opc = ((*mp->b_rptr++ | (*mp->b_rptr++ << 8) | (*mp->b_rptr++ << 16))) & 0x00ffffff;
		m->sls = *mp->b_rptr++;
		if ((m->pvar & SS7_PVAR_YR) != SS7_PVAR_00)
			m->sls &= 0x1f;
		else
			m->sls &= 0xff;
		return (0);
	}
	return (-EPROTO);
}

/*
 *  Decode Service Information Field
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *  All Formats:
 *  --------------------+-------v-------+
 *         signal       |   H1  |  H0   |
 *  --------------------+-------^-------+
 */
static int mtp_dec_sif(mblk_t * mp, mtp_msg_t * m)
{
	unsigned char tag;
	switch (m->si) {
	case 0:		/* SNMM */
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		tag = (*mp->b_rptr << 4) | (*mp->b_rptr >> 4);
		m->h0 = *mp->b_rptr & 0x0f;
		m->h1 = (*mp->b_rptr++ >> 4) & 0x0f;
		switch (tag) {
		case 0x11:	/* coo */
		case 0x12:	/* coa */
			return mtp_dec_com(mp, m);
		case 0x15:	/* cbd */
		case 0x16:	/* cba */
			return mtp_dec_cbm(mp, m);
		case 0x71:	/* tra */
		case 0x72:	/* trw */
		case 0x31:	/* rct */
			return (0);
		case 0x32:	/* tfc */
			return mtp_dec_tfc(mp, m);
		case 0x41:	/* tfp */
		case 0x42:	/* tcp */
		case 0x43:	/* tfr */
		case 0x44:	/* tcr */
		case 0x45:	/* tfa */
		case 0x46:	/* tca */
		case 0x51:	/* rst */
		case 0x52:	/* rsr */
		case 0x53:	/* rcp */
		case 0x54:	/* rcr */
			return mtp_dec_tfm(mp, m);
		case 0x81:	/* dlc */
			return mtp_dec_dlc(mp, m);
		case 0x82:	/* css */
		case 0x83:	/* cns */
		case 0x84:	/* cnp */
		case 0x21:	/* eco */
		case 0x22:	/* eca */
		case 0x61:	/* lin */
		case 0x62:	/* lun */
		case 0x63:	/* lia */
		case 0x64:	/* lua */
		case 0x65:	/* lid */
		case 0x66:	/* lfu */
		case 0x67:	/* llt */
		case 0x68:	/* lrt */
			return mtp_dec_slm(mp, m);
		case 0xa1:	/* upu */
		case 0xa2:	/* upa *//* ansi91 only */
		case 0xa3:	/* upt *//* ansi91 only */
			return mtp_dec_upm(mp, m);
		}
		break;
	case 1:		/* SNTM */
	case 2:		/* SSNTM */
		if (mp->b_wptr < mp->b_rptr + 1)
			break;
		tag = (*mp->b_rptr << 4) | (*mp->b_rptr >> 4);
		m->h0 = *mp->b_rptr & 0x0f;
		m->h1 = (*mp->b_rptr++ >> 4) & 0x0f;
		switch (tag) {
		case 0x11:	/* sltm */
		case 0x12:	/* slta */
			return mtp_dec_sltm(mp, m);
		}
		break;
	default:		/* USER PART */
		return mtp_dec_user(mp, m);
	}
	return (-EPROTO);
}

static int mtp_dec_msg(queue_t * q, mblk_t * mp, mtp_msg_t * m)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	if (mp->b_wptr < mp->b_rptr + 1)
		goto emsgsize;
	m->xq = q;
	m->timestamp = jiffies;
	m->pvar = SS7_PVAR_ETSI_00;
	m->popt = 0;
	if ((err = mtp_dec_sio(mp, m)))
		goto error;
	if (m->ni != sl->ni)
		goto discard;
	if ((err = mtp_dec_rl(mp, m)))
		goto error;
	if ((err = mtp_dec_sif(mp, m)))
		goto error;
	return (0);
      discard:
	return (-EPROTO);
      error:
	ptrace(("%s: %p: DECODE: decoding error\n", MTP_DRV_NAME, sl));
	return (err);
      emsgsize:
	ptrace(("%s: %p: DECODE: decoding error\n", MTP_DRV_NAME, sl));
	return (-EMSGSIZE);
}

static int mtp_recv_msg(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	mtp_msg_t msg = { 0, };
	if ((err = mtp_dec_msg(q, mp, &msg)))
		goto error;
	if (sl->ni != msg.ni)
		goto eproto;
	if (sl->loc != msg.dpc)
		goto eproto;
	if (sl->adj != msg.opc)
		goto eproto;
	/* message is for us, process it */
	return mtp_proc_msg(q, mp, &msg);
      eproto:
	ptrace(("%s: %p: DECODE: message not for us NI=%x, DPC=%x, OPC=%x, SI=%x\n", MTP_DRV_NAME,
		sl, msg.ni, msg.dpc, msg.opc, msg.si));
	return (-EPROTO);
      error:
	ptrace(("%s: %p: DECODE: decoding error %d\n", MTP_DRV_NAME, sl, err));
	return (err);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Primitives from below
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  M_DATA:
 *  -------------------------------------------------------------------
 */
static int sl_data_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	if (sl->state != SLS_IN_SERVICE)
		goto outstate;
	return mtp_recv_msg(q, mp);
      outstate:
	return (QR_DONE);
}

/*
 *  SL_PDU_IND:
 *  -------------------------------------------------------------------
 *  When we receive a PDU from below, we want to decode it and then determine
 *  what to do based on the decoding of the message.
 */
static int sl_pdu_ind(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_pdu_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_IN_SERVICE)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p) || !mp->b_cont)
		goto einval;
	if ((err = mtp_recv_msg(q, mp->b_cont)) == QR_ABSORBED)
		return (QR_TRIMMED);
	return (err);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
}

/*
 *  SL_LINK_CONGESTED_IND:
 *  -------------------------------------------------------------------
 */
static int sl_link_congested_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_link_cong_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_IN_SERVICE)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	if (sl->cong != p->sl_cong_state) {
		/* check route and routeset */
		sl->cong = p->sl_cong_status;
	}
	if (sl->disc != p->sl_disc_state) {
		/* check route and routeset */
		sl->disc = p->sl_disc_status;
	}
	fixme(("Write this function\n"));
	return (QR_DONE);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_LINK_CONGESTION_CEASED_IND:
 *  -------------------------------------------------------------------
 */
static int sl_link_congestion_ceased_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_link_cong_ceased_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_IN_SERVICE)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	if (sl->cong != p->sl_cong_state) {
		/* check route and routeset */
		sl->cong = p->sl_cong_status;
	}
	if (sl->disc != p->sl_disc_state) {
		/* check route and routeset */
		sl->disc = p->sl_disc_status;
	}
	fixme(("Write this function\n"));
	return (QR_DONE);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_RETRIEVED_MESSAGE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_retrieved_message_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_retrieved_msg_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_RETR)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	if (!mp->b_cont)
		goto efault;
	bufq_queue(&sl->sl.buf, mp->b_cont);
	fixme(("Write this function\n"));
	return (QR_TRIMMED);
      efault:
	ptrace(("%s: %p: ERROR: no data\n", MTP_DRV_NAME, m));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_RETRIEVAL_COMPLETE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_retrieval_complete_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_retrieval_comp_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_RETR)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_RB_CLEARED_IND:
 *  -------------------------------------------------------------------
 */
static int sl_rb_cleared_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_rb_cleared_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_CLRB)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_BSNT_IND:
 *  -------------------------------------------------------------------
 */
static int sl_bsnt_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_bsnt_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_BSNT)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_IN_SERVICE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_in_service_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_in_service_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_INSI)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_OUT_OF_SERVICE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_out_of_service_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_out_of_service_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_IN_SERVICE)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_REMOTE_PROCESSOR_OUTAGE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_remote_processor_outage_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_rem_proc_out_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_IN_SERVICE)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_REMOTE_PROCESSOR_RECOVERED_IND:
 *  -------------------------------------------------------------------
 */
static int sl_remote_processor_recovered_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_rem_proc_recovered_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_PROC_OUTG)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_RTB_CLEARED_IND:
 *  -------------------------------------------------------------------
 */
static int sl_rtb_cleared_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_rtb_cleared_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_CLRB)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_RETRIEVAL_NOT_POSSIBLE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_retrieval_not_possible_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_retrieval_not_poss_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_RETR)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_BSNT_NOT_RETRIEVABLE_IND:
 *  -------------------------------------------------------------------
 */
static int sl_bsnt_not_retrievable_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_bsnt_not_retr_ind_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != SLS_WIND_BSNT)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

#if 0
/*
 *  SL_OPTMGMT_ACK:
 *  -------------------------------------------------------------------
 */
static int sl_optmgmt_ack(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_optmgmt_ack_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
	return (-EOPNOTSUPP);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  SL_NOTIFY_IND:
 *  -------------------------------------------------------------------
 */
static int sl_notify_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const sl_notify_ind_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
	return (-EOPNOTSUPP);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}
#endif
/*
 *  LMI_INFO_ACK:
 *  -------------------------------------------------------------------
 */
static int lmi_info_ack(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_info_ack_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_OK_ACK:
 *  -------------------------------------------------------------------
 */
static int lmi_ok_ack(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_ok_ack_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	switch (sl->state) {
	case LMI_ATTACH_PENDING:
		sl_set_state(LMI_DISABLED);
		break;
	case LMI_DETACH_PENDING:
		sl_set_state(LMI_UNATTACHED);
		break;
	default:
		goto outstate;
	}
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_ERROR_ACK:
 *  -------------------------------------------------------------------
 */
static int lmi_error_ack(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_error_ack_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	switch (sl->state) {
	case LMI_ATTACH_PENDING:
		sl_set_state(LMI_UNATTACHED);
		break;
	case LMI_DETACH_PENDING:
	case LMI_ENABLE_PENDING:
		sl_set_state(LMI_DISABLED);
		break;
	case LMI_DISABLE_PENDING:
		sl_set_state(LMI_ENABLED);
		break;
	default:
		goto outstate;
	}
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_ENABLE_CON:
 *  -------------------------------------------------------------------
 */
static int lmi_enable_con(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_enable_con_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != LMI_ENABLED_PENDING)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_DISABLE_CON:
 *  -------------------------------------------------------------------
 */
static int lmi_disable_con(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_disable_con_t *p = (typeof(p)) mp->b_rptr;
	if (sl->state != LMI_DISABLE_PENDING)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	return (-EPROTO);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_OPTMGMT_ACK:
 *  -------------------------------------------------------------------
 */
static int lmi_optmgmt_ack(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_optmgmt_ack_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_ERROR_IND:
 *  -------------------------------------------------------------------
 */
static int lmi_error_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_error_ind_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_STATS_IND:
 *  -------------------------------------------------------------------
 */
static int lmi_stats_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_stats_ind_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  LMI_EVENT_IND:
 *  -------------------------------------------------------------------
 */
static int lmi_event_ind(queue_t * q, mblk_t * mp)
{
#ifdef MTP_COMPLETE
	sl_t *sl = SL_PRIV(q);
	int err;
	const lmi_event_ind_t *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	fixme(("Write this function\n"));
	return (-EFAULT);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive formant\n", MTP_DRV_NAME, m));
	return (-EINVAL);
#endif
	__ptrace(("Unimplemented\n"));
	return (-EFAULT);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Primitives from above
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  M_DATA
 *  -------------------------------------------------------------------
 */
static int mtp_data_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	int dlen = msgdsize(mp);
	if (mtp_get_state(m) == TS_IDLE)
		goto discard;
	if (mtp_get_state(m) != TS_DATA_XFER && mtp_get_state(m) != TS_WREQ_ORDREL)
		goto outstate;
	if (dlen == 0 || dlen > 272)
		goto baddata;
	return mtp_send_msg(m, NULL, &m->dst, mp);
      baddata:
	ptrace(("%s: %p: ERROR: bad data size %d\n", MTP_DRV_NAME, m, dlen));
	goto error;
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      discard:
	ptrace(("%s: %p: ERROR: ignore in idle state\n", MTP_DRV_NAME, m));
	return (QR_DONE);
      error:
	return m_error(q, -EPROTO);
}

/*
 *  T_CONN_REQ
 *  -----------------------------------
 *  As MTP is really a connectionless protocol, when we form a connection we
 *  simply remember the destination address.  Some interim MTPs had the
 *  abilitty to send a UPT (User Part Test) message.  If the protocol variant
 *  has this ability, we wait for the result of the User Part Test before
 *  confirming the connection.
 */
static int t_conn_req(queue_t * q, mblk_t * mp)
{
	mtp_t *mtp = MTP_PRIV(q);
	int err = -EFAULT;
	const struct T_conn_req *p = (typeof(p)) mp->b_rptr;
	size_t mlen = mp->b_wptr > mp->b_rptr ? mp->b_wptr - mp->b_rptr : 0;
	if (mtp_get_state(mtp) != TS_IDLE)
		goto outstate;
	if (mlen < sizeof(*p))
		goto einval;
	if (mlen < p->DEST_offset + p->DEST_length)
		goto einval;
	if (mlen < p->OPT_offest + p->OPT_length)
		goto einval;
	{
		mtp_addr_t *dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		if (dst->mtp_family != AF_MTP)
			goto badaddr;
		if (dst->mtp_si == 0 && mtp->src.mtp_si == 0)
			goto noaddr;
		if (dst->mtp_si < 3 && mtp->cred.cr_uid != 0)
			goto acces;
		if (dst->mtp_si != mtp->src.mtp_si)
			goto baddaddr;
		if (!mtp_check_dst(mtp, dst))
			goto badaddr;
		{
			unsigned char *opt = mp->b_rptr + p->OPT_offset;
			struct mtp_opts opts = { 0L, NULL, };
			if (mtp_parse_opts(mtp, &opts, opt, p->OPT_length))
				goto badopt;
			/* TODO: set options first */
			if (mp->b_cont) {
				putbq(q, mp->b_cont);	/* hold back data */
				mp->b_cont = NULL;	/* abosrbed mp->b_cont */
			}
			mtp->dst = *dst;
			mtp_set_state(mtp, TS_WACK_CREQ);
			return t_ok_ack(q, T_CONN_REQ);
		}
	}
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options\n", MTP_DRV_NAME, m));
	goto error;
      acces:
	err = TACCES;
	ptrace(("%s: %p: ERROR: no permission for address\n", MTP_DRV_NAME, m));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("%s: %p: ERROR: address is unusable\n", MTP_DRV_NAME, m));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: invalid message format\n", MTP_DRV_NAME, m));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      error:
	return t_error_ack(q, T_CONN_REQ, err);
}

/*
 *  T_DISCON_REQ         2 - TC disconnection request
 *  -------------------------------------------------------------------
 */
static int t_discon_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	const struct T_discon_req *p = (typeof(p)) mp->b_rptr;
	if ((1 << mtp_get_state(m)) & ~TSM_CONNECTED)
		goto outstate;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	switch (mtp_get_state(m)) {
	case TS_WCON_CREQ:
		mtp_set_state(m, TS_WACK_DREQ6);
		break;
	case TS_DATA_XFER:
		mtp_set_state(m, TS_WACK_DREQ9);
		break;
	}
	return t_ok_ack(q, T_DISCON_REQ);
      einval:
	err = -EINVAL;
	ptrace(("%s: ERROR: invalid primitive format\n", MTP_DRV_NAME));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: ERROR: would place i/f out of state\n", MTP_DRV_NAME));
	goto error;
      error:
	return t_error_ack(q, T_DISCON_REQ, err);
}

/*
 *  T_DATA_REQ           3 - Connection-Mode data transfer request
 *  -------------------------------------------------------------------
 */
static int t_data_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = PRIV(q);
	int err;
	const struct T_data_req *p = (typeof(p)) mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	if (mtp_get_state(m) == TS_IDLE)
		goto discard;
	if (dlen == 0 || dlen > 272)
		goto baddata;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	if ((1 << mtp_get_state(m)) & ~TSM_OUTDATA)
		goto outstate;
	if ((err = mtp_send_msg(m, NULL, &m->dst, mp->b_cont)) == QR_ABSORBED)
		return (QR_TRIMMED);
	return (err);
      baddata:
	ptrace(("%s: %p: ERROR: bad data size %d\n", MTP_DRV_NAME, m, dlen));
	goto error;
      outstate:
	ptrace(("%s: ERROR: would place i/f out of state\n", MTP_DRV_NAME));
	goto error;
      einval:
	ptrace(("%s: ERROR: invalid primitive format\n", MTP_DRV_NAME));
	goto error;
      discard:
	ptrace(("%s: ERROR: ignore in idle state\n", MTP_DRV_NAME));
	return (QR_DONE);
      error:
	return m_error(q, -EPROTO);
}

/*
 *  T_EXDATA_REQ         4 - Expedited data request
 *  -------------------------------------------------------------------
 */
static int t_exdata_req(queue_t * q, mblk_t * mp)
{
	(void) mp;
	return m_error(q, -EPROTO);
}

/*
 *  T_INFO_REQ           5 - Information Request
 *  -------------------------------------------------------------------
 */
static int t_info_req(queue_t * q, mblk_t * mp)
{
	(void) mp;
	return t_info_ack(q);
}

/*
 *  T_BIND_REQ           6 - Bind a TS user to a transport address
 *  -------------------------------------------------------------------
 */
static int t_bind_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	const struct T_bind_req *p = (typeof(p)) mp->b_rptr;
	if (mtp_get_state != TS_UNBND)
		goto outstate;
	mtp_set_state(m, TS_WACK_BREQ);
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	if (p->CONIND_number)
		goto notsupport;
	if ((mp->b_wptr < mp->b_rptr + p->ADDR_offset + p->ADDR_lenth) || (p->ADDR_length != sizeof(mtp_addr_t)))
		goto badaddr;
	{
		mtp_addr_t *src = (typeof(src)) (mp->b_rptr + p->ADDR_offset);
		/* we don't allow wildcards yet. */
		if (src->mtp_family != AF_MTP)
			goto badaddr;
		if (!src->si || !src->pc)
			goto noaddr;
		if (src->si < 3 && m->cred.cr_uid != 0)
			goto acces;
		if ((err = mtp_check_src(m, src)))
			goto error;
		return t_bind_ack(q, src);
	}
      acces:
	err = TACCES;
	ptrace(("%s: %p: ERROR: no permission for address\n", MTP_DRV_NAME, m));
	goto error;
      noaddr:
	err = TNOADDR;
	ptrace(("%s: %p: ERROR: couldn't allocate address\n", MTP_DRV_NAME, m));
	goto error;
      badaddr:
	err = TBADADDR;
	ptrace(("%s: %p: ERROR: address is invalid\n", MTP_DRV_NAME, m));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: invalid primitive format\n", MTP_DRV_NAME, m));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      notsupport:
	err = TNOTSUPPORT;
	ptrace(("%s: %p: ERROR: primitive not support for T_CLTS\n", MTP_DRV_NAME, m));
	goto error;
      error:
	return t_error_ack(q, T_BIND_REQ, err);
}

/*
 *  T_UNBIND_REQ         7 - Unbind TS user from transport address
 *  -------------------------------------------------------------------
 */
static int t_unbind_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	if (mtp_get_state(m) != TS_IDLE)
		goto outstate;
	mtp_set_state(m, TS_WACK_UREQ);
	return t_ok_ack(q, T_UNBIND_REQ);
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      error:
	return t_error_ack(q, T_UNBIND_REQ, err);
}

/*
 *  T_UNITDATA_REQ       8 -Unitdata Request 
 *  -------------------------------------------------------------------
 */
static int t_unitdata_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	const struct T_unitdata_req *p = (typeof(p)) mp->b_rptr;
	size_t dlen = mp->b_cont ? msgdsize(mp->b_cont) : 0;
	if (mtp_get_state(m) != TS_IDLE)
		goto outstate;
	if (dlen == 0)
		goto baddata;
	if (dlen > 272)
		goto baddata;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p)
	    || mp->b_wptr < mp->b_rptr + p->DEST_offset + p->DEST_length
	    || mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
		goto einval;
	else {
		mtp_addr_t *dst = (typeof(dst)) (mp->b_rptr + p->DEST_offset);
		if (p->DEST_length < sizeof(*dst))
			goto badaddr;
		if (dst->si < 3 && m->cred.cr_uid != 0)
			goto acces;
		if (!mtp_check_dst(m, dst))
			goto badaddr;
		else {
			struct mtp_opts opts = { 0L, NULL, };
			if (mtp_parse_opts(m, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
				goto badopt;
			if ((err = mtp_send_msg(m, &opts, dst, mp->b_cont)) == QR_ABSORBED)
				return (QR_TRIMMED);
			return (err);
		}
	}
      badopt:
	ptrace(("%s: %p: ERROR: bad options\n", MTP_DRV_NAME, m));
	goto error;
      acces:
	ptrace(("%s: %p: ERROR: no permission to address\n", MTP_DRV_NAME, m));
	goto error;
      badadd:
	ptrace(("%s: %p: ERROR: bad destination address\n", MTP_DRV_NAME, m));
	goto error;
      einval:
	ptrace(("%s: %p: ERROR: invalid parameter\n", MTP_DRV_NAME, m));
	goto error;
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      baddata:
	ptrace(("%s: %p: ERROR: bad data size %d\n", MTP_DRV_NAME, m, dlen));
	goto error;
      error:
	return m_error(q, -EPROTO);
}

/*
 *  T_OPTMGMT_REQ        9 - Options management request
 *  -------------------------------------------------------------------
 */
static int t_optmgmt_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err = 0;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optmgmt_req *p = (typeof(p)) mp->b_rptr;
#ifdef TS_WACK_OPTREQ
	if (mtp_get_state(m) == TS_IDLE)
		mtp_set_state(m, TS_WACK_OPTREQ);
#endif
	if (mp->b_wptr < mp->b_rptr + sizeof(*p)
	    || mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
		goto einval;
	{
		struct mtp_opts opts = { 0L, NULL, };
		if (mtp_parse_opts(m, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
		switch (p->MGMT_flags) {
		case T_CHECK:
			err = mtp_opt_check(m, &opts);
			break;
		case T_NEGOTIATE:
			if (!opts.flags)
				mtp_opt_default(m, &opts);
			else if ((err = mtp_opt_check(m, &opts)))
				break;
			err = mtp_opt_negotiate(m, &opts);
			break;
		case T_DEFAULT:
			err = mtp_opt_default(m, &opts);
			break;
		case T_CURRENT:
			err = mtp_opt_current(m, &opts);
			break;
		default:
			goto badflag;
		}
		if (err)
			goto provspec;
		return t_optmgmt_ack(q, p->MGMT_flags, &opts);
	}
      provspec:
	err = err;
	ptrace(("%s: %p: ERROR: provider specific\n", MTP_DRV_NAME, m));
	goto error;
      badflag:
	err = TBADFLAG;
	ptrace(("%s: %p: ERROR: bad options flags\n", MTP_DRV_NAME, m));
	goto error;
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options\n", MTP_DRV_NAME, m));
	goto error;
      einval:
	err = -EINVAL;
	ptrace(("%s: %p: ERROR: invalid primitive format\n", MTP_DRV_NAME, m));
	goto error;
      error:
	return t_error_ack(q, T_OPTMGMT_REQ, err);
}

/*
 *  T_ORDREL_REQ        10 - TS user is finished sending
 *  -------------------------------------------------------------------
 */
static int t_ordrel_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = PRIV(q);
	const struct T_ordrel_req *p = (typeof(p)) mp->b_rptr;
	if ((1 << mtp_get_state(m)) & ~TSM_OUTDATA)
		goto outstate;
	switch (mtp_get_state(m)) {
	case TS_DATA_XFER:
		mtp_set_state(m, TS_WIND_ORDREL);
		break;
	case TS_WREQ_ORDREL:
		goto error;
	}
	return t_ordrel_ind(q);
      outstate:
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      error:
	return m_error(q, EPROTO);
}

/*
 *  T_OPTDATA_REQ       24 - Data with options request
 *  -------------------------------------------------------------------
 */
static int t_optdata_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	const size_t mlen = mp->b_wptr - mp->b_rptr;
	const struct T_optdata_req *p = (typeof(p)) mp->b_rptr;
	if (mtp_get_state(m) == TS_IDLE)
		goto discard;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p)
	    || mp->b_wptr < mp->b_rptr + p->OPT_offset + p->OPT_length)
		goto einval;
	if ((1 << mtp_get_state(m)) & ~TSM_OUTDATA)
		goto outstate;
	if (p->DATA_flags & T_ODF_EX || p->DATA_flags & T_ODF_MORE)
		goto notsupport;
	else {
		struct mtp_opts opts = { 0L, NULL, };
		if (mtp_parse_opts(m, &opts, mp->b_rptr + p->OPT_offset, p->OPT_length))
			goto badopt;
		if ((err = mtp_send_msg(m, &opts, &m->dst, mp->b_cont)) == QR_ABSORBED)
			return (QR_TRIMMED);
		return (err);
	}
      badopt:
	err = TBADOPT;
	ptrace(("%s: %p: ERROR: bad options\n", MTP_DRV_NAME, m));
	goto error;
      outstate:
	err = TOUTSTATE;
	ptrace(("%s: %p: ERROR: would place i/f out of state\n", MTP_DRV_NAME, m));
	goto error;
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive format\n", MTP_DRV_NAME, m));
	return m_error(q, EPROTO);
      discard:
	ptrace(("%s: %p: ERROR: ignore in idle state\n", MTP_DRV_NAME, m));
	return (QR_DONE);
      error:
	return t_error_ack(q, T_OPTDATA_REQ, err);
}

#ifdef T_ADDR_REQ
/*
 *  T_ADDR_REQ          25 - Address Request
 *  -------------------------------------------------------------------
 */
static int t_addr_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = PRIV(q);
	(void) mp;
	switch (mtp_get_state(m)) {
	case TS_UNBND:
		return t_addr_ack(q, NULL, NULL);
	case TS_IDLE:
		return t_addr_ack(q, &m->src, NULL);
	case TS_WCON_CREQ:
	case TS_DATA_XFER:
	case TS_WIND_ORDREL:
	case TS_WREQ_ORDREL:
		return t_addr_ack(q, &m->src, &m->dst);
	case TS_WRES_CIND:
		return t_addr_ack(q, NULL, &m->dst);
	}
	return t_error_ack(q, T_ADDR_REQ, TOUTSTATE);
}
#endif

#ifdef T_CAPABILITY_REQ
/*
 *  T_CAPABILITY_REQ    ?? - Capability Request
 *  -------------------------------------------------------------------
 */
static int t_capability_req(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	int err;
	const struct T_capability_req *p = (typeof(p)) mp->b_rptr;
	if (mp->b_wptr < mp->b_rptr + sizeof(*p))
		goto einval;
	return t_capability_ack(q, p->CAP_bits1);
      einval:
	ptrace(("%s: %p: ERROR: invalid primitive format\n", MTP_DRV_NAME, m));
	return t_error_ack(q, T_CAPABILITY_REQ, -EINVAL);
}
#endif

/*
 *  -------------------------------------------------------------------------
 *
 *  IO Controls
 *
 *  -------------------------------------------------------------------------
 */
/*
 *  MTP_IOCGOPTION  - lmi_ioption_t
 *  -------------------------------------------------------------------
 *  Gets the current default protocol variant and options for Signalling
 *  Points.  The default default is SS7_PVAR_ETSI_00 with no protocol options.
 */
static int mtp_iocgoption(queue_t * q, mblk_t * mp)
{
	mblk_t *dp;
	if ((dp = mp->b_cont)) {
		lmi_option_t *arg = (typeof(arg)) dp->b_rptr;
		mtp_t *mtp = MTP_PRIV(q);
		int ret = 0;
		int flags = 0;
		if (dp->b_wptr < dp->b_rptr + sizeof(*arg))
			goto einval;
		*arg = mtp_default_option;
		return (ret);
	}
      einval:
	rare();
	return (-EINVAL);
}

/*
 *  MTP_IOCSOPTION  - lmi_ioption_t
 *  -------------------------------------------------------------------
 *  Sets the default protocol variant and options for Signalling Points.  The
 *  default default is SS7_PVAR_ETSI_00 with no protocol options.
 */
static int mtp_iocsoption(queue_t * q, mblk_t * mp)
{
	mblk_t *dp;
	if ((dp = mp->b_cont)) {
		lmi_option_t *arg = (typeof(arg)) dp->b_rptr;
		mtp_t *mtp = MTP_PRIV(q);
		int ret = 0;
		int flags = 0;
		if (dp->b_wptr < dp->b_rptr + sizeof(*arg))
			goto einval;
		switch (arg->pvar) {
		case SS7_PVAR_ITUT_88:
		case SS7_PVAR_ITUT_93:
		case SS7_PVAR_ITUT_96:
		case SS7_PVAR_ITUT_00:
		case SS7_PVAR_ETSI_88:
		case SS7_PVAR_ETSI_93:
		case SS7_PVAR_ETSI_96:
		case SS7_PVAR_ETSI_00:
		case SS7_PVAR_ANSI_88:
		case SS7_PVAR_ANSI_92:
		case SS7_PVAR_ANSI_96:
		case SS7_PVAR_ANSI_00:
		case SS7_PVAR_JTTC_94:
		case SS7_PVAR_CHIN_00:
			/* these are fully supported */
			break;
		default:
			todo(("Implement and test other protocol variants\n"));
			goto einval;
		}
		mtp_default_option = *arg;;
		return (ret);
	}
      einval:
	rare();
	return (-EINVAL);
}

/*
 *  MTP_IOCGCONFIG  - mtp_config_t
 *  -------------------------------------------------------------------
 */
static inline int mtp_iocgconfig(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCSCONFIG  - mtp_config_t
 *  -------------------------------------------------------------------
 */

static int mtp_iocsconfig(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCTCONFIG  - mtp_config_t
 *  -------------------------------------------------------------------
 */
static int mtp_ioctconfig(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCCCONFIG  - mtp_config_t
 *  -------------------------------------------------------------------
 */
static int mtp_ioccconfig(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCGSTATEM  - mtp_statem_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocgstatem(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCCMRESET  - mtp_statem_t
 *  -------------------------------------------------------------------
 */
static int mtp_ioccmreset(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCGSTATSP  - mtp_stats_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocgstatsp(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCSSTATSP  - mtp_stats_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocsstatsp(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCGSTATS   - mtp_stats_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocgstats(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCSSTATS   - mtp_stats_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocsstats(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCGNOTIFY  - mtp_notify_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocgnotify(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCSNOTIFY  - mtp_notify_t
 *  -------------------------------------------------------------------
 */
static int mtp_iocsnotify(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCCNOTIFY  - mtp_notify_t
 *  -------------------------------------------------------------------
 */
static int mtp_ioccnotify(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCCMGMT    - mtp_ctl_t
 *  -------------------------------------------------------------------
 */
static int mtp_ioccmgmt(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  MTP_IOCCPASS    - mtp_ulong
 *  -------------------------------------------------------------------
 */
static int mtp_ioccpass(queue_t * q, mblk_t * mp)
{
	return (-EOPNOTSUPP);
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  M_IOCTL Handling
 *
 *  -------------------------------------------------------------------------
 */
static int mtp_w_ioctl(queue_t * q, mblk_t * mp)
{
	mtp_t *m = MTP_PRIV(q);
	struct iocblk *iocp = (struct iocblk *) 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(cmn), nr = _IOC_NR(cmd), size = _IOC_SIZE(cmd);
	int ret = 0;
	switch (type) {
	case __SID:
	{
		sl_t *sl;
		struct linkblk *lb;
		if (!(lb = arg)) {
			swerr();
			ret = (-EINVAL);
			break;
		}
		switch (nr) {
		case _IOC_NR(I_PLINK):
			if (iocp->ioc_cr->cr_uid != 0) {
				ptrace(("%s: %p: ERROR: Non-root attempt to I_PLINK\n", MTP_DRV_NAME, m));
				ret = -EPERM;
				break;
			}
		case _IOC_NR(I_LINK):
			if ((sl = sl_alloc_priv(lb->l_qbot, &sl_list, lb->l_index, iocp->ioc_cr)))
				break;
			ret = -ENOMEM;
			break;
		case _IOC_NR(I_PUNLINK):
			if (iocp->ioc_cr->cr_uid != 0) {
				ptrace(("%s: %p: ERROR: Non-root attempt to I_PUNLINK\n", MTP_DRV_NAME, m));
				ret = -EPERM;
				break;
			}
		case _IOC_NR(I_UNLINK):
			for (sl = sl_list; sl; sl = sl->next)
				if (sl->u.mux.index == lb->l_index)
					break;
			if (!sl) {
				ret = -EINVAL;
				ptrace(("%s: %p: ERROR: Couldn't find I_UNLINK muxid\n"));
				break;
			}
			sl_free_priv(sl->rq);
			break;
		default:
		case _IOC_NR(I_STR):
			ptrace(("%s: %p: ERROR: Unsupported STREAMS ioctl %d\n", MTP_DRV_NAME, nr));
			ret = (-EOPNOTSUPP);
			break;
		}
		break;
	}
	case MTP_IOC_MAGIC:
	{
		if (count < size) {
			ret = (-EINVAL);
			break;
		}
		switch (nr) {
		case _IOC_NR(MTP_IOCGOPTION):	/* lmi_option_t */
			rtn = mtp_iocgoption(q, mp);
			break;
		case _IOC_NR(MTP_IOCSOPTION):	/* lmi_option_t */
			rtn = mtp_iocsoption(q, mp);
			break;
		case _IOC_NR(MTP_IOCGCONFIG):	/* mtp_config_t */
			rtn = mtp_iocgconfig(q, mp);
			break;
		case _IOC_NR(MTP_IOCSCONFIG):	/* mtp_config_t */
			rtn = mtp_iocsconfig(q, mp);
			break;
		case _IOC_NR(MTP_IOCTCONFIG):	/* mtp_config_t */
			rtn = mtp_ioctconfig(q, mp);
			break;
		case _IOC_NR(MTP_IOCCCONFIG):	/* mtp_config_t */
			rtn = mtp_ioccconfig(q, mp);
			break;
		case _IOC_NR(MTP_IOCGSTATEM):	/* mtp_statem_t */
			rtn = mtp_iocgstatem(q, mp);
			break;
		case _IOC_NR(MTP_IOCCMRESET):	/* mtp_statem_t */
			rtn = mtp_ioccmreset(q, mp);
			break;
		case _IOC_NR(MTP_IOCGSTATSP):	/* mtp_stats_t */
			rtn = mtp_iocgstatsp(q, mp);
			break;
		case _IOC_NR(MTP_IOCSSTATSP):	/* mtp_stats_t */
			rtn = mtp_iocsstatsp(q, mp);
			break;
		case _IOC_NR(MTP_IOCGSTATS):	/* mtp_stats_t */
			rtn = mtp_iocgstats(q, mp);
			break;
		case _IOC_NR(MTP_IOCSSTATS):	/* mtp_stats_t */
			rtn = mtp_iocsstats(q, mp);
			break;
		case _IOC_NR(MTP_IOCGNOTIFY):	/* mtp_notify_t */
			rtn = mtp_iocgnotify(q, mp);
			break;
		case _IOC_NR(MTP_IOCSNOTIFY):	/* mtp_notify_t */
			rtn = mtp_iocsnotify(q, mp);
			break;
		case _IOC_NR(MTP_IOCCNOTIFY):	/* mtp_notify_t */
			rtn = mtp_ioccnotify(q, mp);
			break;
		case _IOC_NR(MTP_IOCCMGMT):	/* mtp_ctl_t */
			rtn = mtp_ioccmgmt(q, mp);
			break;
		case _IOC_NR(MTP_IOCCPASS):	/* mtp_ulong */
			rtn = mtp_ioccpass(q, mp);
			break;
		default:
			ptrace(("%s: %d: ERROR: Unsupported MTP ioctl %d\n", nr));
			ret = (-EOPNOTSUPP);
			break;
		}
		break;
	}
	default:
		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_IOCACK, M_IOCNAK Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sl_r_iocack(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	mblk_t *dp;
	queue_t *mq;
	mtp_t *mtp;
	struct iocblk *iocp = (typeof(iocp)) mp->b_rptr;
	struct mtp_pass *pass = mp->b_cont ? ((typeof(pass)) mp->b_cont->b_rptr) - 1 : NULL;
	if (!mtp_lmq || !(lm = LM_PRIV(mtp_lmq)) || lm->ioc_id != iocp->ioc_id)
		goto discard;
	lm->ioc_id == NULL;
	if (!(dp = mp->b_cont) || dp->b_rptr <= dp->b_datap->db_base + sizeof(*pass))
		goto discard;
	dp->b_rptr -= sizeof(*pass);
	for (mq = mtp_list; mq && mq != *(typeof(mq) *) dp->b_rptr; mq = mq->next) ;
	if (!mq || mq->ioc_id != iocp->ioc_id)
		goto discard;
	pass->pass_cmd = xchg(&iocp->ioc_cmd, MTP_IOCPASS);
	putnext(lm->rq, mp);
	return (QR_ABSORBED);
      discard:
	return (QR_DONE);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_PROTO, M_PCPROTO Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sl_r_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	str_t *s = PRIV(q);
	ulong oldstate = s->state;
	if ((prim = *(ulong *) mp->b_rptr) == SL_PDU_IND) {
		printd(("%s: %p: SL_PDU_IND [%d] <-\n", MTP_DRV_NAME, s, msgdsize(mp->b_cont)));
		if ((rtn = sl_pdu_ind(q, mp)) < 0)
			s->state = oldstate;
		return (rtn);
	}
	switch (prim) {
	case SL_PDU_IND:
		printd(("%s: %p: SL_PDU_IND [%d] <-\n", MTP_DRV_NAME, s, msgdsize(mp->b_cont)));
		rtn = sl_pdu_ind(q, mp);
		break;
	case SL_LINK_CONGESTED_IND:
		printd(("%s: %p: SL_LINK_CONGESTED_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_link_congested_ind(q, mp);
		break;
	case SL_LINK_CONGESTION_CEASED_IND:
		printd(("%s: %p: SL_LINK_CONGESTION_CEASED_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_link_congestion_ceased_ind(q, mp);
		break;
	case SL_RETRIEVED_MESSAGE_IND:
		printd(("%s: %p: SL_RETRIEVED_MESSAGE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_retrieved_message_ind(q, mp);
		break;
	case SL_RETRIEVAL_COMPLETE_IND:
		printd(("%s: %p: SL_RETRIEVAL_COMPLETE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_retrieval_complete_ind(q, mp);
		break;
	case SL_RB_CLEARED_IND:
		printd(("%s: %p: SL_RB_CLEARED_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_rb_cleared_ind(q, mp);
		break;
	case SL_BSNT_IND:
		printd(("%s: %p: SL_BSNT_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_bsnt_ind(q, mp);
		break;
	case SL_IN_SERVICE_IND:
		printd(("%s: %p: SL_IN_SERVICE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_in_service_ind(q, mp);
		break;
	case SL_OUT_OF_SERVICE_IND:
		printd(("%s: %p: SL_OUT_OF_SERVICE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_out_of_service_ind(q, mp);
		break;
	case SL_REMOTE_PROCESSOR_OUTAGE_IND:
		printd(("%s: %p: SL_REMOTE_PROCESSOR_OUTAGE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_remote_processor_outage_ind(q, mp);
		break;
	case SL_REMOTE_PROCESSOR_RECOVERED_IND:
		printd(("%s: %p: SL_REMOTE_PROCESSOR_RECOVERED_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_remote_processor_recovered_ind(q, mp);
		break;
	case SL_RTB_CLEARED_IND:
		printd(("%s: %p: SL_RTB_CLEARED_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_rtb_cleared_ind(q, mp);
		break;
	case SL_RETRIEVAL_NOT_POSSIBLE_IND:
		printd(("%s: %p: SL_RETRIEVAL_NOT_POSSIBLE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_retrieval_not_possible_ind(q, mp);
		break;
	case SL_BSNT_NOT_RETRIEVABLE_IND:
		printd(("%s: %p: SL_BSNT_NOT_RETRIEVABLE_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_bsnt_not_retrievable_ind(q, mp);
		break;
#if 0
	case SL_OPTMGMT_ACK:
		printd(("%s: %p: SL_OPTMGMT_ACK <-\n", MTP_DRV_NAME, s));
		rtn = sl_optmgmt_ack(q, mp);
		break;
	case SL_NOTIFY_IND:
		printd(("%s: %p: SL_NOTIFY_IND <-\n", MTP_DRV_NAME, s));
		rtn = sl_notify_ind(q, mp);
		break;
#endif
	case LMI_INFO_ACK:
		printd(("%s: %p: LMI_INFO_ACK <-\n", MTP_DRV_NAME, s));
		rtn = lmi_info_ack(q, mp);
		break;
	case LMI_OK_ACK:
		printd(("%s: %p: LMI_OK_ACK <-\n", MTP_DRV_NAME, s));
		rtn = lmi_ok_ack(q, mp);
		break;
	case LMI_ERROR_ACK:
		printd(("%s: %p: LMI_ERROR_ACK <-\n", MTP_DRV_NAME, s));
		rtn = lmi_error_ack(q, mp);
		break;
	case LMI_ENABLE_CON:
		printd(("%s: %p: LMI_ENABLE_CON <-\n", MTP_DRV_NAME, s));
		rtn = lmi_enable_con(q, mp);
		break;
	case LMI_DISABLE_CON:
		printd(("%s: %p: LMI_DISABLE_CON <-\n", MTP_DRV_NAME, s));
		rtn = lmi_disable_con(q, mp);
		break;
	case LMI_OPTMGMT_ACK:
		printd(("%s: %p: LMI_OPTMGMT_ACK <-\n", MTP_DRV_NAME, s));
		rtn = lmi_optmgmt_ack(q, mp);
		break;
	case LMI_ERROR_IND:
		printd(("%s: %p: LMI_ERROR_IND <-\n", MTP_DRV_NAME, s));
		rtn = lmi_error_ind(q, mp);
		break;
	case LMI_STATS_IND:
		printd(("%s: %p: LMI_STATS_IND <-\n", MTP_DRV_NAME, s));
		rtn = lmi_stats_ind(q, mp);
		break;
	case LMI_EVENT_IND:
		printd(("%s: %p: LMI_EVENT_IND <-\n", MTP_DRV_NAME, s));
		rtn = lmi_event_ind(q, mp);
		break;
	default:
		/* reject what we don't recognize */
		printd(("%s: %p: ???? <-\n", MTP_DRV_NAME, s));
		rtn = -EOPNOTSUPP;
		break;
	}
	if (rtn < 0)
		s->state = oldstate;
	return (rtn);
}

static int mtp_w_proto(queue_t * q, mblk_t * mp)
{
	int rtn;
	ulong prim;
	str_t *s = PRIV(q);
	ulong oldstate = s->state;
	switch ((prim = *(ulong *) mp->b_rptr)) {
	case T_CONN_REQ:
		printd(("%s: %p: -> T_CONN_REQ\n", MTP_DRV_NAME, s));
		rtn = t_conn_req(q, mp);
		break;
	case T_CONN_RES:
		printd(("%s: %p: -> T_CONN_RES\n", MTP_DRV_NAME, s));
		rtn = t_error_ack(q, prim, TNOTSUPPORT);
		break;
	case T_DISCON_REQ:
		printd(("%s: %p: -> T_DISCON_REQ\n", MTP_DRV_NAME, s));
		rtn = t_discon_req(q, mp);
		break;
	case T_DATA_REQ:
		printd(("%s: %p: -> T_DATA_REQ\n", MTP_DRV_NAME, s));
		rtn = t_data_req(q, mp);
		break;
	case T_EXDATA_REQ:
		printd(("%s: %p: -> T_EXDATA_REQ\n", MTP_DRV_NAME, s));
		rtn = t_exdata_req(q, mp);
		break;
	case T_INFO_REQ:
		printd(("%s: %p: -> T_INFO_REQ\n", MTP_DRV_NAME, s));
		rtn = t_info_req(q, mp);
		break;
	case T_BIND_REQ:
		printd(("%s: %p: -> T_BIND_REQ\n", MTP_DRV_NAME, s));
		rtn = t_bind_req(q, mp);
		break;
	case T_UNBIND_REQ:
		printd(("%s: %p: -> T_UNBIND_REQ\n", MTP_DRV_NAME, s));
		rtn = t_unbind_req(q, mp);
		break;
	case T_UNITDATA_REQ:
		printd(("%s: %p: -> T_UNITDATA_REQ\n", MTP_DRV_NAME, s));
		rtn = t_unitdata_req(q, mp);
		break;
	case T_OPTMGMT_REQ:
		printd(("%s: %p: -> T_OPTMGMT_REQ\n", MTP_DRV_NAME, s));
		rtn = t_optmgmt_req(q, mp);
		break;
	case T_ORDREL_REQ:
		printd(("%s: %p: -> T_ORDREL_REQ\n", MTP_DRV_NAME, s));
		rtn = t_ordrel_req(q, mp);
		break;
	case T_OPTDATA_REQ:
		printd(("%s: %p: -> T_OPTDATA_REQ\n", MTP_DRV_NAME, s));
		rtn = t_optdata_req(q, mp);
		break;
#ifdef T_ADDR_REQ
	case T_ADDR_REQ:
		printd(("%s: %p: -> T_ADDR_REQ\n", MTP_DRV_NAME, s));
		rtn = t_addr_req(q, mp);
		break;
#endif
#ifdef T_CAPABILITY_REQ
	case T_CAPABILITY_REQ:
		printd(("%s: %p: -> T_CAPABILITY_REQ\n", MTP_DRV_NAME, s));
		rtn = t_capability_req(q, mp);
		break;
#endif
	default:
		rtn = t_error_ack(q, prim, TNOTSUPPORT);
		break;
	}
	if (rtn < 0)
		s->state = oldstate;
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_DATA Handling
 *
 *  -------------------------------------------------------------------------
 */
static int sl_r_data(queue_t * q, mblk_t * mp)
{
	return sl_data_ind(q, mp);
}
static int mtp_w_data(queue_t * q, mblk_t * mp)
{
	return mtp_data_req(q, mp);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_RSE, M_PCRSE Handling
 *
 *  -------------------------------------------------------------------------
 */
static int mtp_r_rse(queue_t * q, mblk_t * mp)
{
	str_t *s = PRIV(q);
	int rtn;
	int flags;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		switch (*(ulong *) mp->b_rptr) {
		case t1t:
			printd(("%s: %p: t1t expiry at %lu\n", MTP_DRV_NAME, s, jiffies));
			rtn = sl_t1t_timeout(q);
			break;
		case t2t:
			printd(("%s: %p: t1t expiry at %lu\n", MTP_DRV_NAME, s, jiffies));
			rtn = sl_t2t_timeout(q);
			break;
		default:
			swerr();
			rtn = -EFAULT;
			break;
		}
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	return (rtn);
}
static int sl_w_rse(queue_t * q, mblk_t * mp)
{
	str_t *s = PRIV(q);
	int rtn;
	int flags;
	lis_spin_lock_irqsave(&s->lock, &flags);
	{
		switch (*(ulong *) mp->b_rptr) {
		case t1:
			printd(("%s: %p: t1 expiry at %lu\n", MTP_DRV_NAME, s, jiffies));
			rtn = sl_t1_expiry(q);
			break;
		default:
			swerr();
			rtn = -EFAULT;
			break;
		}
	}
	lis_spin_unlock_irqrestore(&s->lock, &flags);
	return (rtn);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_ERROR Handling
 *
 *  -------------------------------------------------------------------------
 *  A hangup from below indicates that a signalling link has failed badly.
 *  Move link to the out-of-service state, notify management, and perform
 *  normal link failure actions.
 */
static int sl_r_error(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	sl->state = -*mp->b_rptr;
	fixme(("Complete this function\n"));
	return (-EFAULT);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_HANGUP Handling
 *
 *  -------------------------------------------------------------------------
 *  A hangup from below indicates that a signalling link has failed badly.
 *  Move link to the out-of-service state, notify management, and perform
 *  normal link failure actions.
 */
static int sl_r_hangup(queue_t * q, mblk_t * mp)
{
	sl_t *sl = SL_PRIV(q);
	sl->state = -EPIPE;
	fixme(("Complete this function\n"));
	return (-EFAULT);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  M_FLUSH Handling
 *
 *  -------------------------------------------------------------------------
 */
static int mtp_r_flush(queue_t * q, mblk_t * mp)
{
	str_t *s = PRIV(q);
	if (*mp->b_rptr & FLUSHW) {
		/* just in case upper doesn't loop properly */
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->wq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->wq, FLUSHALL);
	}
	if (*mp->b_rptr & FLUSHR) {
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->rq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->rq, FLUSHALL);
	}
	if (*mp->b_rptr & (FLUSHR | FLUSHW)) {
		putnext(q, mp);
		return (QR_ABSORBED);
	}
	return (QR_DONE);
}
static int mtp_w_flush(queue_t * q, mblk_t * mp)
{
	str_t *s = PRIV(q);
	if (*mp->b_rptr & FLUSHW) {
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->wq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->wq, FLUSHALL);
		*mp->b_rptr &= ~FLUSHW;
	}
	if (*mp->b_rptr & FLUSHR) {
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->rq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->rq, FLUSHALL);
		qreply(q, mp);
		return (QR_ABSORBED);
	}
	return (QR_DONE);
}
static int sl_r_flush(queue_t * q, mblk_t * mp)
{
	str_t *s = PRIV(q);
	if (*mp->b_rptr & FLUSHR) {
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->rq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->rq, FLUSHALL);
		*mp->b_rptr &= ~FLUSHR;
	}
	if (*mp->b_rptr & FLUSHW) {
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->wq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->wq, FLUSHALL);
		qreply(q, mp);
		return (QR_ABSORBED);
	}
	return (QR_DONE);
}
static int sl_w_flush(queue_t * q, mblk_t * mp)
{
	str_t *s = PRIV(q);
	if (*mp->b_rptr & FLUSHR) {
		/* just in case lower doesn't loop properly */
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->rq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->rq, FLUSHALL);
	}
	if (*mp->b_rptr & FLUSHW) {
		if (*mp->b_rtpr & FLUSHBAND)
			flushband(s->wq, mp->b_rptr[1], FLUSHALL);
		else
			flushq(s->wq, FLUSHALL);
	}
	if (*mp->b_rptr & (FLUSHR | FLUSHW)) {
		putnext(q, mp);
		return (QR_ABSORBED);
	}
	return (QR_DONE);
}

/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
static int mtp_r_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_RSE:
	case M_PCRSE:
		return mtp_r_rse(q, mp);
	case M_FLUSH:
		return mtp_r_flush(q, mp);
	}
	return (QR_PASSFLOW);
}
static int mtp_w_prim(queue_t * q, mblk_t * mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return mtp_w_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return mtp_w_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return mtp_w_proto(q, mp);
	case M_FLUSH:
		return mtp_w_flush(q, mp);
	case M_IOCTL:
		return mtp_w_ioctl(q, mp);
	}
	return (QR_PASSFLOW);
}
static int sl_r_prim(queue_t * q, mblk_t * mp)
{
	/* Fast Path */
	if (mp->b_datap->db_type == M_DATA)
		return sl_r_data(q, mp);
	switch (mp->b_datap->db_type) {
	case M_DATA:
		return sl_r_data(q, mp);
	case M_PROTO:
	case M_PCPROTO:
		return sl_r_proto(q, mp);
	case M_FLUSH:
		return sl_r_flush(q, mp);
	case M_ERROR:
		return sl_r_error(q, mp);
	case M_HANGUP:
		return sl_r_hangup(q, mp);
	case M_IOCACK:
	case M_IOCNAK:
		return sl_r_iocack(q, mp);
	}
	return (QR_PASSFLOW);
}
static int sl_w_prim(queue_t * q, mblk_t * mp)
{
	switch (mp->b_datap->db_type) {
	case M_RSE:
	case M_PCRSE:
		return sl_w_rse(q, mp);
	case M_FLUSH:
		return sl_w_flush(q, mp);
	}
	return (QR_PASSFLOW);
}

/*
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 *
 *  PUTP Put Routine
 *  -----------------------------------
 */
static int mtp_putp(queue_t * q, mblk_t * mp, int (*proc) (queue_t *, mblk_t *), int (*wakeup) (queue_t *))
{
	int rtn = 0;
	ensure(q, return (-EFAULT));
	ensure(mp, return (-EFAULT));
	if (mp->b_datap->db_type < QPCTL || q->q_count) {
		putq(q, mp);
		return (0);
	}
	if (mtp_trylock(q)) {
		do {
			/* Fast Path */
			if ((rtn = (*proc) (q, mp)) == QR_ABSORBED)
				break;
			switch (rtn) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				break;
			case QR_STRIP:
				if (mp->b_cont)
					putq(q, mp->b_cont);
			case QR_TRIMMED:
				freeb(mp);
				break;
			case QR_LOOP:
				if (!q->q_next) {
					qreply(q, mp);
					break;
				}
			case QR_PASSALONG:
				if (q->q_next) {
					putnext(q, mp);
					break;
				}
				rtn = -EOPNOTSUPP;
			default:
				freemsg(mp);
				break;
			case QR_DISABLE:
				putq(q, mp);
				rtn = 0;
				break;
			case QR_PASSFLOW:
				if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
					putnext(q, mp);
					break;
				}
			case -ENOBUFS:
			case -EBUSY:
			case -EAGAIN:
			case -ENOBUFS:
				putq(q, mp);
				break;
			}
		}
		while (0);
		if (wakeup)
			wakeup(q);
		mtp_unlockq(q);
	} else {
		rare();
		putq(q, mp);
	}
	return (rtn);
}

/*
 *  SRVP Put Routine
 *  -----------------------------------
 */
static int mtp_srvp(queue_t * q, int (*proc) (queue_t *, mblk_t *), int (*wakeup) (queue_t *))
{
	int rtn = 0;
	ensure(q, return (-EFAULT));
	if (mtp_trylockq(q)) {
		mblk_t *mp;
		while ((mp = getq(q))) {
			/* Fast Path */
			if ((rtn = proc(q, mp)) == QR_ABSORBED)
				continue;
			switch (rtn) {
			case QR_DONE:
				freemsg(mp);
			case QR_ABSORBED:
				continue;
			case QR_STRIP:
				if (mp->b_cont)
					putq(q, mp->b_cont);
			case QR_TRIMMED:
				freeb(mp);
				continue;
			case QR_LOOP:
				if (!q->q_next) {
					qreply(q, mp);
					continue;
				}
			case QR_PASSALONG:
				if (q->q_next) {
					putnext(q, mp);
					continue;
				}
				rtn = -EOPNOTSUPP;
			default:
				printd(("%s: %p: ERROR: (q dropping) %d\n",
					q->q_qinfo->qi_minfo->mi_idname, PRIV(q), rtn));
				freemsg(mp);
				continue;
			case QR_DISABLE:
				printd(("%s: %p: ERROR: (q disabling) %d\n",
					q->q_qinfo->qi_minfo->mi_idname, PRIV(q), rtn));
				noenable(q);
				putbq(q, mp);
				rtn = 0;
				break;
			case QR_PASSFLOW:
				if (mp->b_datap->db_type >= QPCTL || canputnext(q)) {
					putnext(q, mp);
					continue;
				}
			case -ENOBUFS:	/* proc must have scheduled bufcall */
			case -EBUSY:	/* proc must have failed canput */
			case -ENOMEM:	/* proc must have scheduled bufcall */
			case -EAGAIN:	/* proc must re-enable on some event */
				if (mp->b_datap->db_type < QPCTL) {
					printd(("%s: %p: ERROR: (q stalled) %d\n",
						q->q_qinfo->qi_minfo->mi_idname, PRIV(q), rtn));
					putbq(q, mp);
					break;
				}
				/* Be careful not to put a priority message back on the queue. */
				if (mp->b_datap->db_type == M_PROTO) {
					mp->b_datap->db_type = M_PROTO;
					mp->b_band = 255;
					putq(q, mp);
				}
				printd(("%s: %p: ERROR: (q discarding) %d\n",
					q->q_qinfo->qi_minfo->mi_idname, PRIV(q), rtn));
				freemsg(mp);
				continue;
			}
			break;
		}
		if (wakeup)
			wakeup(q);
		mtp_unlockq(q);
	} else
		rare();
	return (rtn);
}

/*
 *  PUT and SRV functions
 *  -----------------------------------
 */
static int mtp_rput(queue_t * q, mblk_t * mp)
{
	return mtp_putp(q, mp, &mtp_r_prim, &mtp_r_wakeup);
}
static int mtp_rsrv(queue_t * q)
{
	return mtp_srvp(q, &mtp_r_prim, &mtp_r_wakeup);
}
static int mtp_wput(queue_t * q, mblk_t * mp)
{
	return mtp_putp(q, mp, &mtp_w_prim, &mtp_w_wakeup);
}
static int mtp_wsrv(queue_t * q)
{
	return mtp_srvp(q, &mtp_w_prim, &mtp_w_wakeup);
}
static int sl_rput(queue_t * q, mblk_t * mp)
{
	return mtp_putp(q, mp, &sl_r_prim, &sl_r_wakeup);
}
static int sl_rsrv(queue_t * q)
{
	return mtp_srvp(q, &sl_r_prim, &sl_r_wakeup);
}
static int sl_wput(queue_t * q, mblk_t * mp)
{
	return mtp_putp(q, mp, &sl_w_prim, &sl_w_wakeup);
}
static int sl_wsrv(queue_t * q)
{
	return mtp_srvp(q, &sl_w_prim, &sl_w_wakeup);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *
 *  OPEN
 *  -------------------------------------------------------------------------
 */
static int mtp_open(queue_t * q, dev_t * devp, int flag, int sflag, cred_t * crp)
{
	int flags;
	int cmajor = getmajor(*devp);
	int cminor = getminor(*devp);
	mtp_t *mtp, **mtpp = &mtp_list;
	MOD_INC_USE_COUNT;	/* keep module from unloading in our face */
	if (q->q_ptr != NULL) {
		MOD_DEC_USE_COUNT;
		return (0);	/* already open */
	}
	if (sflag == MODOPEN || WR(q)->q_next) {
		ptrace(("%s: ERROR: can't push as module\n", MTP_DRV_NAME));
		MOD_DEC_USE_COUNT;
		return (EIO);
	}
	if (!cminor) {
		sflag = CLONEOPEN;
		cminor = 1;
	}
	for (; *mtpp; mtpp = &(*mtpp)->next) {
		int dminor = getminor((*mtpp)->u.dev);
		if (cminor < dminor)
			break;
		if (cminor == dminor) {
			if (sflag == CLONEOPEN)
				if (++cminor < MTP_NMINOR)
					continue;
			ptrace(("%s: ERROR: device in use\n", MTP_DRV_NAME));
			MOD_DEC_USE_COUNT;
			return (EIO);
		}
	}
	if (cminor >= MTP_NMAJOR) {
		ptrace(("%s: ERROR: no device numbers available\n", MTP_DRV_NAME));
		MOD_DEC_USE_COUNT;
		return (ENXIO);
	}
	printd(("%s: opened character device %d:%d\n", MTP_DRV_NAME, cmajor, cminor));
	*devp = makedevice(cmajor, cminor);
	if (!(mtp = mtp_alloc_priv(q, mtpp, devp, crp))) {
		ptrace(("%s: ERROR: No memory\n", MTP_DRV_NAME));
		MOD_DEC_USE_COUNT;
		return (ENOMEM);
	}
	return (0);
}

/*
 *  CLOSE
 *  -------------------------------------------------------------------------
 */
static int mtp_close(queue_t * q, int flag, cred_t * crp)
{
	mtp_t *mtp = PRIV(q);
	int flags;
	(void) flag;
	(void) crp;
	(void) mtp;
	printd(("%s: closing character device %d:%d\n", MTP_DRV_NAME, mtp->u.dev.cmajor, mtp->u.dev.cminor));
	mtp_free_priv(q);
	MOD_DEC_USE_COUNT;
	return (0);
}

/*
 *  =========================================================================
 *
 *  LiS Module Initialization
 *
 *  =========================================================================
 */
static int mtp_initialized = 0;
static void mtp_init(void)
{
	int err;
	cmn_err(CE_NOTE, MTP_BANNER);	/* console splash */
	if ((err = mtp_init_caches())) {
		cmn_err(CE_PANIC, "%s: ERROR: Counld not allocated caches", MTP_DRV_NAME);
		mtp_initialized = err;
		return;
	}
	if ((err = lis_register_strdev(MTP_CMAJOR, &mtp_info, MTP_NMINOR, MTP_DRV_NAME)) < 0) {
		cmn_err(CE_PANIC, "%s: Can't register major %d", MTP_DRV_NAME, MTP_CMAJOR);
		mtp_term_caches();
		mtp_initialized = err;
		return;
	}
	mtp_initialized = 1;
	return;
}
static void mtp_terminate(void)
{
	int err;
	if (mtp_initialized > 0) {
		if ((err = lis_unregister_strdev(MTP_CMAJOR)))
			cmn_err(CE_PANIC, "%s: Can't unregister major %d\n", MTP_DRV_NAME, MTP_CMAJOR);
		mtp_term_caches();
		mtp_initialized = 0;
	}
	return;
}

/*
 *  =========================================================================
 *
 *  LINUX MODULE INITIALIZATION
 *
 *  =========================================================================
 */
int init_module(void)
{
	mtp_init();
	if (mtp_initialized < 0)
		return mtp_initialized;
	return (0);
}
void cleanup_module(void)
{
	mtp_terminate();
}


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

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

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