/*****************************************************************************

 @(#) File: src/drivers/sl_x400p.c

 -----------------------------------------------------------------------------

 Copyright (c) 2008-2015  Monavacon Limited <http://www.monavacon.com/>
 Copyright (c) 2001-2008  OpenSS7 Corporation <http://www.openss7.com/>
 Copyright (c) 1997-2001  Brian F. G. Bidulock <bidulock@openss7.org>

 All Rights Reserved.

 This program is free software: you can redistribute it and/or modify it under
 the terms of the GNU Affero General Public License as published by the Free
 Software Foundation, version 3 of the license.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>, or
 write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
 02139, USA.

 -----------------------------------------------------------------------------

 U.S. GOVERNMENT RESTRICTED RIGHTS.  If you are licensing this Software on
 behalf of the U.S. Government ("Government"), the following provisions apply
 to you.  If the Software is supplied by the Department of Defense ("DoD"), it
 is classified as "Commercial Computer Software" under paragraph 252.227-7014
 of the DoD Supplement to the Federal Acquisition Regulations ("DFARS") (or any
 successor regulations) and the Government is acquiring only the license rights
 granted herein (the license rights customarily provided to non-Government
 users).  If the Software is supplied to any unit or agency of the Government
 other than DoD, it is classified as "Restricted Computer Software" and the
 Government's rights in the Software are defined in paragraph 52.227-19 of the
 Federal Acquisition Regulations ("FAR") (or any successor regulations) or, in
 the cases of NASA, in paragraph 18.52.227-86 of the NASA Supplement to the FAR
 (or any successor regulations).

 -----------------------------------------------------------------------------

 Commercial licensing and support of this software is available from OpenSS7
 Corporation at a fee.  See http://www.openss7.com/

 *****************************************************************************/

static char const ident[] = "src/drivers/sl_x400p.c (" PACKAGE_ENVR ") " PACKAGE_DATE;

/*
 *  This is an SL (Signalling Link) kernel module which provides all of the
 *  capabilities of the SLI for the E400P-SS7 and T400P-SS7 cards.  This is a
 *  complete SS7 MTP Level 2 OpenSS7 implementation.
 */

//#define _DEBUG 1
#undef _DEBUG

#define _MPS_SOURCE	1
#define _SVR4_SOURCE	1
#define _SUN_SOURCE	1
#define _HPUX_SOURCE	1

#define X400P_DOWNLOAD_FIRMWARE 1

#include <sys/os7/compat.h>

#ifndef KBUILD_MODNAME
#define KBUILD_MODNAME KBUILD_BASENAME
#endif

#ifdef LINUX
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/pci.h>

#include <linux/interrupt.h>
#ifndef _MPS_SOURCE
#if 0
#include "bufpool.h"
#else
#include <sys/os7/bufpool.h>
#endif
#endif				/* _MPS_SOURCE */
#endif				/* LINUX */

#include <linux/if.h>
#include <net/lif.h>
#include <net/nit_if.h>
#include <net/bpf.h>

#undef LMI_DEFAULT

#include <ss7/lmi.h>
#include <ss7/lmi_ioctl.h>
#undef _MPS_SOURCE		/* get the right sdl/sdt/sl_timers_t */
#include <ss7/sdli.h>
#include <ss7/sdli_ioctl.h>
#include <ss7/sdti.h>
#include <ss7/sdti_ioctl.h>
#include <ss7/sli.h>
#include <ss7/sli_ioctl.h>
#define _MPS_SOURCE 1
#include <sys/mxi.h>
#include <sys/mxi_ioctl2.h>
#include <sys/dsx_ioctl.h>

#include <sys/dlpi.h>
#include <sys/dlpi_ioctl.h>

#ifdef X400P_DOWNLOAD_FIRMWARE
#include "v401pfw.h"
#endif

#define SL_X400P_DESCRIP	"X400P-SS7: SS7/SL (Signalling Link) STREAMS Driver"
#define SL_X400P_EXTRA		"Part of the OpenSS7 Stack for Linux Fast-STREAMS"
#define SL_X400P_REVISION	"OpenSS7 src/drivers/sl_x400p.c (" PACKAGE_ENVR ") " PACKAGE_DATE
#define SL_X400P_COPYRIGHT	"Copyright (c) 2008-2015  Monavacon Limited.  All Rights Reserved."
#define SL_X400P_DEVICE		"Supports the V40XP E1/T1/J1 (Tormenta II/III) PCI boards."
#define SL_X400P_CONTACT	"Brian Bidulock <bidulock@openss7.org>"
#define SL_X400P_LICENSE	"GPL"
#define SL_X400P_BANNER		SL_X400P_DESCRIP	"\n" \
				SL_X400P_EXTRA		"\n" \
				SL_X400P_REVISION	"\n" \
				SL_X400P_COPYRIGHT	"\n" \
				SL_X400P_DEVICE		"\n" \
				SL_X400P_CONTACT
#define SL_X400P_SPLASH		SL_X400P_DESCRIP	" - " \
				SL_X400P_REVISION

#ifdef LINUX
MODULE_AUTHOR(SL_X400P_CONTACT);
MODULE_DESCRIPTION(SL_X400P_DESCRIP);
MODULE_SUPPORTED_DEVICE(SL_X400P_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(SL_X400P_LICENSE);
#endif				/* MODULE_LICENSE */
#if defined MODULE_ALIAS
MODULE_ALIAS("streams-sl_x400p");
#endif
#if defined MODULE_VERSION
MODULE_VERSION(PACKAGE_ENVR);
#endif
#endif				/* LINUX */

#ifndef CONFIG_STREAMS_SL_X400P_NAME
#define CONFIG_STREAMS_SL_X400P_NAME    	"x400p-sl"
#endif
#ifndef CONFIG_STREAMS_SL_X400P_MODID
#define CONFIG_STREAMS_SL_X400P_MODID   	2083
#endif
#ifndef CONFIG_STREAMS_SL_X400P_NMINORS
#define CONFIG_STREAMS_SL_X400P_NMINORS 	1
#endif
#ifndef CONFIG_STREAMS_SL_X400P_NMAJORS
#define CONFIG_STREAMS_SL_X400P_NMAJORS 	1
#endif
#ifndef CONFIG_STREAMS_SL_X400P_MAJOR
#define CONFIG_STREAMS_SL_X400P_MAJOR   	2083
#endif
#ifndef CONFIG_STREAMS_SL_X400P_MAJOR_0
#define CONFIG_STREAMS_SL_X400P_MAJOR_0 	2083
#endif
#ifndef CONFIG_STREAMS_SL_X400P_MODULE
#define CONFIG_STREAMS_SL_X400P_MODULE  	1
#endif

#define SL_X400P_DRV_ID		CONFIG_STREAMS_SL_X400P_MODID
#define SL_X400P_DRV_NAME	CONFIG_STREAMS_SL_X400P_NAME
#define SL_X400P_CMAJORS	CONFIG_STREAMS_SL_X400P_NMAJORS
#define SL_X400P_CMAJOR_0	CONFIG_STREAMS_SL_X400P_MAJOR
#define SL_X400P_UNITS		CONFIG_STREAMS_SL_X400P_NMINORS

#ifdef LINUX
#ifdef MODULE_ALIAS
MODULE_ALIAS("streams-modid-" __stringify(CONFIG_STREAMS_SL_X400P_MODID));
MODULE_ALIAS("streams-driver-sl-x400p");
MODULE_ALIAS("streams-major-" __stringify(CONFIG_STREAMS_SL_X400P_MAJOR));
MODULE_ALIAS("/dev/streams/x400p-sl");
MODULE_ALIAS("/dev/streams/x400p-sl/*");
MODULE_ALIAS("/dev/streams/clone/x400p-sl");
MODULE_ALIAS("char-major-" __stringify(CONFIG_STREAMS_CLONE_MAJOR) "-" __stringify(SL_X400P_CMAJOR_0));
MODULE_ALIAS("/dev/x400p-sl");
//MODULE_ALIAS("devname:x400p-sl");
#endif				/* MODULE_ALIAS */
#endif				/* LINUX */

#ifdef _OPTIMIZE_SPEED
#define X400LOG(sid, level, flags, fmt, ...) \
	do { } while (0)
#else
#define X400LOG(sid, level, flags, fmt, ...) \
	strlog(SL_X400P_DRV_ID, sid, level, flags, fmt, ##__VA_ARGS__)
#endif

#define STRLOGERR	0	/* log X400 error information */
#define STRLOGNO	0	/* log X400 notice information */
#define STRLOGST	1	/* log X400 state transitions */
#define STRLOGTO	2	/* log X400 timeouts */
#define STRLOGRX	3	/* log X400 primitives received */
#define STRLOGTX	4	/* log X400 primitives issued */
#define STRLOGTE	5	/* log X400 timer events */
#define STRLOGIO	6	/* log X400 additional data */
#define STRLOGDA	7	/* log X400 data */

#define LOGERR(sid, fmt, ...) X400LOG(sid, STRLOGERR, SL_TRACE | SL_ERROR | SL_CONSOLE, fmt, ##__VA_ARGS__)
#define LOGNO(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGST(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGTO(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGRX(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGTX(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGTE(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGIO(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)
#define LOGDA(sid, fmt, ...)  X400LOG(sid, STRLOGNO,  SL_TRACE, fmt, ##__VA_ARGS__)

#define ch2sid(ch) (ch->ppa + (1<<12))
#define xp2sid(xp) (xp->cminor)

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

#define DRV_ID		SL_X400P_DRV_ID
#define DRV_NAME	SL_X400P_DRV_NAME
#define CMAJORS		SL_X400P_CMAJORS
#define CMAJOR_0	SL_X400P_CMAJOR_0
#define UNITS		SL_X400P_UNITS
#ifdef MODULE
#define DRV_BANNER	SL_X400P_BANNER
#else				/* MODULE */
#define DRV_BANNER	SL_X400P_SPLASH
#endif				/* MODULE */

STATIC struct module_info xp_minfo = {
	.mi_idnum = DRV_ID,	/* Module ID number */
	.mi_idname = DRV_NAME,	/* Module name */
	.mi_minpsz = 1,		/* Min packet size accepted */
	.mi_maxpsz = INFPSZ,	/* Max packet size accepted */
	.mi_hiwat = 1024,	/* Hi water mark */
	.mi_lowat = 0,		/* Lo water mark */
};

STATIC struct module_stat xp_rstat __attribute__ ((aligned(SMP_CACHE_BYTES)));
STATIC struct module_stat xp_wstat __attribute__ ((aligned(SMP_CACHE_BYTES)));

STATIC streamscall int xp_qopen(queue_t *, dev_t *, int, int, cred_t *);
STATIC streamscall int xp_qclose(queue_t *, int, cred_t *);

STATIC streamscall int xp_rput(queue_t *, mblk_t *);
STATIC streamscall int xp_rsrv(queue_t *);

STATIC struct qinit xp_rinit = {
	.qi_putp = xp_rput,	/* Read put (message from below) */
	.qi_srvp = xp_rsrv,	/* Read queue service */
	.qi_qopen = xp_qopen,	/* Each open */
	.qi_qclose = xp_qclose,	/* Last close */
	.qi_minfo = &xp_minfo,	/* Information */
	.qi_mstat = &xp_rstat,	/* Statistics */
};

STATIC streamscall int xp_wput(queue_t *, mblk_t *);
STATIC streamscall int xp_wsrv(queue_t *);

STATIC struct qinit xp_winit = {
	.qi_putp = xp_wput,	/* Write put (message from above) */
	.qi_srvp = xp_wsrv,	/* Write queue service */
	.qi_minfo = &xp_minfo,	/* Information */
	.qi_mstat = &xp_wstat,	/* Statistics */
};

STATIC struct streamtab sl_x400pinfo = {
	.st_rdinit = &xp_rinit,	/* Upper read queue */
	.st_wrinit = &xp_winit,	/* Upper write queue */
};

/*
 *  ========================================================================
 *
 *  Private structure
 *
 *  ========================================================================
 */
struct xp;
struct ch;
struct sp;
struct cd;

typedef struct xp_path {
	uint residue;			/* residue bits */
	uint rbits;			/* number of residue bits */
	ushort bcc;			/* crc for message */
	uint state;			/* state */
	uint mode;			/* path mode */
	uint type;			/* path frame type */
	uint bytes;			/* number of whole bytes */
	mblk_t *msg;			/* message */
	mblk_t *nxt;			/* message chain block */
	mblk_t *cmp;			/* compression/repeat message */
	uint repeat;			/* compression/repeat count */
	uint octets;			/* octets counted */
} xp_path_t;

typedef struct xp {
	queue_t *rq;			/* read queue */
	queue_t *wq;			/* write queue */
	uint i_state;			/* interface state */
	uint i_flags;			/* interface flags */
	uint i_style;			/* interface style */
	uint i_version;			/* interface version */
	uint i_oldstate;		/* interface saved state */
	major_t cmajor;			/* major device number */
	minor_t cminor;			/* minor device number */
	minor_t bminor;			/* opened minor number */
	struct ch *ch;			/* channel for this interface */
	uint ppa;			/* attached ppa */
	int card, span, chan;		/* attached card span chan */
	int monitor;			/* monitoring point */
#define XP_MONITOR_NONE		0
#define XP_MONITOR_GLOB		1
#define XP_MONITOR_CARD		2
#define XP_MONITOR_SPAN		3
#define XP_MONITOR_CHAN		4
	int level;			/* connection level */
#define XP_LEVEL_MON_SDT	0
#define XP_LEVEL_MON_SL		1
#define XP_LEVEL_MON_SDL	2
#define XP_LEVEL_ACT_SDT	3
#define XP_LEVEL_ACT_SL		4
#define XP_LEVEL_ACT_SDL	5
	struct {
		struct xp **prev;	/* prev next pointer in xray list */
		struct xp *next;	/* next in xray list */
		uint flags;		/* xray flags */
		atomic_t drops;		/* xray drops */
		struct {
			uint flags;	/* NIT options flags */
#ifndef NI_TIMESTAMP
#define NI_PROMISC	(1<<0)
#define NI_TIMESTAMP	(1<<1)
#define NI_LEN		(1<<2)
#define NI_DROPS	(1<<3)
#define NI_USERBITS	(NI_PROMISC|NI_TIMESTAMP|NI_LEN|NI_DROPS)
#endif
			uint snap;	/* snapshot length */
		} nit;
		struct {
			uint flags;	/* BPF options flags */
#define BPF_PROMISC	(1<<0)
#define BPF_SEESENT	(1<<1)
#define BPF_HDRCMPLT	(1<<2)
#define BPF_FEEDBACK	(1<<3)
			uint dlt;	/* data link type */
			uint direction;	/* direction */
			struct bpf_stat stats;	/* stats - part bpfmod */
		} bpf;
	} xray;
} xp_t;

#define XP_PRIV(__q) ((struct xp *)(__q)->q_ptr)

#define XP_XRAY_SL_RX_GLOB	(1<< 4)	/* a global xray rx stream exists */
#define XP_XRAY_SL_RX_CARD	(1<< 5)	/* a card xray rx stream exists */
#define XP_XRAY_SL_RX_SPAN	(1<< 6)	/* a span xray rx stream exists */
#define XP_XRAY_SL_RX_CHAN	(1<< 7)	/* a chan xray rx stream exists */

#define XP_XRAY_SL_TX_GLOB	(1<< 8)	/* a global xray tx stream exists */
#define XP_XRAY_SL_TX_CARD	(1<< 9)	/* a card xray tx stream exists */
#define XP_XRAY_SL_TX_SPAN	(1<<10)	/* a span xray tx stream exists */
#define XP_XRAY_SL_TX_CHAN	(1<<11)	/* a chan xray tx stream exists */

#define XP_XRAY_SDT_RX_GLOB	(1<<12)	/* a global xray rx stream exists */
#define XP_XRAY_SDT_RX_CARD	(1<<13)	/* a card xray rx stream exists */
#define XP_XRAY_SDT_RX_SPAN	(1<<14)	/* a span xray rx stream exists */
#define XP_XRAY_SDT_RX_CHAN	(1<<15)	/* a chan xray rx stream exists */

#define XP_XRAY_SDT_TX_GLOB	(1<<16)	/* a global xray tx stream exists */
#define XP_XRAY_SDT_TX_CARD	(1<<17)	/* a card xray tx stream exists */
#define XP_XRAY_SDT_TX_SPAN	(1<<18)	/* a span xray tx stream exists */
#define XP_XRAY_SDT_TX_CHAN	(1<<19)	/* a chan xray tx stream exists */

#define XP_XRAY_SDL_RX_GLOB	(1<<20)	/* a global xray rx stream exists */
#define XP_XRAY_SDL_RX_CARD	(1<<21)	/* a card xray rx stream exists */
#define XP_XRAY_SDL_RX_SPAN	(1<<22)	/* a span xray rx stream exists */
#define XP_XRAY_SDL_RX_CHAN	(1<<23)	/* a chan xray rx stream exists */

#define XP_XRAY_SDL_TX_GLOB	(1<<24)	/* a global xray tx stream exists */
#define XP_XRAY_SDL_TX_CARD	(1<<25)	/* a card xray tx stream exists */
#define XP_XRAY_SDL_TX_SPAN	(1<<26)	/* a span xray tx stream exists */
#define XP_XRAY_SDL_TX_CHAN	(1<<27)	/* a chan xray tx stream exists */

#define XP_XRAY_SL_GLOB		(XP_XRAY_SL_RX_GLOB|XP_XRAY_SL_TX_GLOB)
#define XP_XRAY_SL_CARD		(XP_XRAY_SL_RX_CARD|XP_XRAY_SL_TX_CARD)
#define XP_XRAY_SL_SPAN		(XP_XRAY_SL_RX_SPAN|XP_XRAY_SL_TX_SPAN)
#define XP_XRAY_SL_CHAN		(XP_XRAY_SL_RX_CHAN|XP_XRAY_SL_TX_CHAN)

#define XP_XRAY_SDT_GLOB	(XP_XRAY_SDT_RX_GLOB|XP_XRAY_SDT_TX_GLOB)
#define XP_XRAY_SDT_CARD	(XP_XRAY_SDT_RX_CARD|XP_XRAY_SDT_TX_CARD)
#define XP_XRAY_SDT_SPAN	(XP_XRAY_SDT_RX_SPAN|XP_XRAY_SDT_TX_SPAN)
#define XP_XRAY_SDT_CHAN	(XP_XRAY_SDT_RX_CHAN|XP_XRAY_SDT_TX_CHAN)

#define XP_XRAY_SDL_GLOB	(XP_XRAY_SDL_RX_GLOB|XP_XRAY_SDL_TX_GLOB)
#define XP_XRAY_SDL_CARD	(XP_XRAY_SDL_RX_CARD|XP_XRAY_SDL_TX_CARD)
#define XP_XRAY_SDL_SPAN	(XP_XRAY_SDL_RX_SPAN|XP_XRAY_SDL_TX_SPAN)
#define XP_XRAY_SDL_CHAN	(XP_XRAY_SDL_RX_CHAN|XP_XRAY_SDL_TX_CHAN)

#define XP_XRAY_GLOB		(XP_XRAY_SL_GLOB|XP_XRAY_SDT_GLOB|XP_XRAY_SDL_GLOB)
#define XP_XRAY_CARD		(XP_XRAY_SL_CARD|XP_XRAY_SDT_CARD|XP_XRAY_SDL_CARD)
#define XP_XRAY_SPAN		(XP_XRAY_SL_SPAN|XP_XRAY_SDT_SPAN|XP_XRAY_SDL_SPAN)
#define XP_XRAY_CHAN		(XP_XRAY_SL_CHAN|XP_XRAY_SDT_CHAN|XP_XRAY_SDL_CHAN)

#define XP_XRAY_SL_RX		(XP_XRAY_SL_RX_GLOB|XP_XRAY_SL_RX_CARD|XP_XRAY_SL_RX_SPAN|XP_XRAY_SL_RX_CHAN)
#define XP_XRAY_SL_TX		(XP_XRAY_SL_TX_GLOB|XP_XRAY_SL_TX_CARD|XP_XRAY_SL_TX_SPAN|XP_XRAY_SL_TX_CHAN)

#define XP_XRAY_SDT_RX		(XP_XRAY_SDT_RX_GLOB|XP_XRAY_SDT_RX_CARD|XP_XRAY_SDT_RX_SPAN|XP_XRAY_SDT_RX_CHAN)
#define XP_XRAY_SDT_TX		(XP_XRAY_SDT_TX_GLOB|XP_XRAY_SDT_TX_CARD|XP_XRAY_SDT_TX_SPAN|XP_XRAY_SDT_TX_CHAN)

#define XP_XRAY_SDL_RX		(XP_XRAY_SDL_RX_GLOB|XP_XRAY_SDL_RX_CARD|XP_XRAY_SDL_RX_SPAN|XP_XRAY_SDL_RX_CHAN)
#define XP_XRAY_SDL_TX		(XP_XRAY_SDL_TX_GLOB|XP_XRAY_SDL_TX_CARD|XP_XRAY_SDL_TX_SPAN|XP_XRAY_SDL_TX_CHAN)

#define XP_XRAY_RX		(XP_XRAY_SL_RX|XP_XRAY_SDT_RX|XP_XRAY_SDL_RX)
#define XP_XRAY_TX		(XP_XRAY_SL_TX|XP_XRAY_SDT_TX|XP_XRAY_SDL_TX)

/*
 * This structure gets rid of a problem that we used to have that the channel configuration would be
 * lost when a stream on the driver was closed.
 *
 * This also opens the possibilty to leave signalling links running while there is no Stream
 * associated with them.  Processor outage can be used to bridge the gap between the upper layer
 * reopening or otherwise resetting the stream.  The processor outage condition can be set on stream
 * closure.  This would involve separating the receive buffer from the read queue and making timers
 * run without a queue (i.e. use simple timeout(9) and put(9)).
 */
typedef struct ch {
	atomic_t refcnt;		/* reference count */
	spinlock_t lock;		/* structure lock */
	struct sp *sp;			/* span for this channel */
	uint ppa;			/* really only 16 bits */
	int chan;			/* index (chan) */
	xp_path_t tx;			/* transmit path variables */
	xp_path_t rx;			/* receive path variables */
	struct xp *xp;			/* active stream for this channel */
	struct {
		struct xp *list;	/* xraying streams for this channel */
		uint flags;		/* xray flags for speed */
	} xray;
	lmi_option_t option;		/* LMI protocol and variant options */
	struct {
		bufq_t rb;		/* received buffer */
		bufq_t tb;		/* transmission buffer */
		bufq_t rtb;		/* retransmission buffer */
		sl_timers_t timers;	/* SL protocol timers */
		sl_config_t config;	/* SL configuration */
		sl_statem_t statem;	/* SL state machine */
		sl_notify_t notify;	/* SL notification options */
		sl_stats_t stats;	/* SL statistics */
		sl_stats_t stamp;	/* SL statistics timestamps */
		sl_stats_t statsp;	/* SL statistics periods */
	} sl;
	struct {
		bufq_t tb;		/* transmission buffer */
		sdt_timers_t timers;	/* SDT protocol timers */
		sdt_config_t config;	/* SDT configuration */
		sdt_statem_t statem;	/* SDT state machine */
		sdt_notify_t notify;	/* SDT notification options */
		sdt_stats_t stats;	/* SDT statistics */
		sdt_stats_t stamp;	/* SDT statistics timestamps */
		sdt_stats_t statsp;	/* SDT statistics periods */
	} sdt;
	struct {
		bufq_t tb;		/* transmission buffer */
		sdl_timers_t timers;	/* SDL protocol timers */
		sdl_config_t config;	/* SDL configuration */
		sdl_statem_t statem;	/* SDL state machine variables */
		sdl_notify_t notify;	/* SDL notification options */
		sdl_stats_t stats;	/* SDL statistics */
		sdl_stats_t stamp;	/* SDL statistics timestamps */
		sdl_stats_t statsp;	/* SDL statistics periods */
	} sdl;
} ch_t;

STATIC void xp_init_ch(struct sp *, struct ch *, uint8_t);
STATIC struct ch *xp_alloc_ch(struct sp *, uint8_t);
STATIC void xp_free_ch(struct ch *);
STATIC struct ch *ch_get(struct ch *);
STATIC void ch_put(struct ch *);
STATIC struct ch *ch_find(uint);

typedef struct st {
	uint32_t SECs;			/* elapsed [seconds] */
	uint32_t ESs;			/* errored [seconds] */
	uint32_t SESs;			/* severely errored [seconds] */
	uint32_t SEFSs;			/* severly errored framing [seconds] */
	uint32_t UASs;			/* unavailable [seconds] */
	uint32_t CSSs;			/* controlled slip [seconds] */
	uint32_t PCVs;			/* path coding [violations] */
	uint32_t LESs;			/* line errored [seconds] */
	uint32_t BESs;			/* bursty errored [seconds] */
	uint32_t DMs;			/* degraded [minutes] */
	uint32_t LCVs;			/* line coding [violations] */
	uint32_t FASEs;			/* frame alignment [errors] */
	uint32_t FABEs;			/* error bit [errors] */
	uint32_t FEBEs;			/* far end block errors [errors] */
	uint32_t ValidData;		/* valid data */
} st_t;

typedef struct sp {
	atomic_t refcnt;		/* reference count */
	spinlock_t lock;		/* structure lock */
	struct cd *cd;			/* card for this span */
	struct ch *chans[32];		/* channels for this span */
	struct xp *xray;		/* xraying streams for this span */
	ulong interval;
	ulong recovertime;		/* alarm recover time */
	ulong iobase;			/* span iobase */
	int span;			/* index (span) */
	volatile ulong loopcnt;		/* loop command count */
	sdl_config_t config;		/* span configuration */
	struct {
		uint count;
		uint state;		/* autoconfig state number */
		uint flags;		/* state flags */
#define		XP_STATE_TOCD		(1<< 0)	/* tx open circuit */
#define		XP_STATE_TSCD		(1<< 1)	/* tx short circuit */
#define		XP_STATE_LOLITC		(1<< 2)	/* tx i/f loss of tx clock */
#define		XP_STATE_LOTC		(1<< 3)	/* tx loss of tx clock */
#define		XP_STATE_LRCL		(1<< 8)	/* rx line i/f carrier loss */
#define		XP_STATE_ILUT		(1<< 9)	/* rx input level under thresh */
#define		XP_STATE_RLOS		(1<<10)	/* rx loss of sync */
#define		XP_STATE_FRCL		(1<<11)	/* rx framer carrier loss */
#define		XP_STATE_RUA1		(1<<12)	/* rx unframed all ones */
#define		XP_STATE_RYEL		(1<<13)	/* rx yellow alarm */
#define		XP_STATE_RRA		(1<<14)	/* rx remote alarm */
#define		XP_STATE_LORC		(1<<15)	/* rx loss of rx clock */
#define		XP_STATE_LUP		(1<<16)	/* rx loop up code */
#define		XP_STATE_LDN		(1<<17)	/* rx loop down code */
#define		XP_STATE_SPARE		(1<<18)	/* rx spare code */
#define		XP_STATE_RDMA		(1<<19)	/* rx dist MF alarm */
#define		XP_STATE_RSAZ		(1<<20)	/* rx sign all zeros */
#define		XP_STATE_RSAO		(1<<21)	/* rx sign all ones */
#define		XP_STATE_RAIS		(1<<22)	/* rx AIS-CI */
#define		XP_STATE_B8ZS		(1<<23)	/* rx B8ZS codeword */
#define		XP_STATE_16ZD		(1<<24)	/* rx 16-zeros */
#define		XP_STATE_8ZD		(1<<25)	/* rx 8-zeros */
#define		XP_STATE_JALT		(1<<26)	/* rx JA limit trip */
#define		XP_STATE_LIRST		(1<<27)	/* line interface reset */
#define		XP_STATE_CRC		(1<<28)
#define		XP_STATE_CAS		(1<<29)
#define		XP_STATE_FAS		(1<<30)
#define		XP_DEF_AIS		(XP_EVENT_RPDV)
#define		XP_DEF_OOF		(XP_XXX_SEFE|XP_XXX_COFA)
#define		XP_FAIL_AIS		(XP_STATE_RUA1)
#define		XP_FAIL_FEA		(XP_STATE_RYEL|XP_STATE_RRA)
#define		XP_FAIL_FELOM		(XP_STATE_RDMA)
#define		XP_FAIL_LPF		(XP_STATE_LUP|XP_STATE_LDN|XP_STATE_SPARE)
#define		XP_FAIL_LOF
#define		XP_FAIL_LOM
#define		XP_FAIL_LOS
#define		XP_FAIL_T16AIS
		uint errors;		/* error conditions */
#define		XP_ERR_SES		(1<< 0)	/* severe error */
#define		XP_ERR_SEFS		(1<< 1)	/* severely errored frame */
#define		XP_ERR_UAS		(1<< 2)	/* unavailable */
#define		XP_ERR_CSS		(1<< 3)	/* controlled slip */
#define		XP_ERR_PCV		(1<< 4)	/* path code violation */
#define		XP_ERR_LES		(1<< 5)	/* line error */
#define		XP_ERR_BES		(1<< 6)	/* bursty error */
#define		XP_ERR_DM		(1<< 7)	/* degraded */
#define		XP_ERR_LCV		(1<< 8)	/* line code violation */
#define		XP_ERR_OOF		(1<< 9)	/* out of frame */
#define		XP_ERR_FASE		(1<<10)	/* frame alignment signal */
#define		XP_ERR_FABE		(1<<11)	/* e-bit error */
#define		XP_ERR_B8ZS		(1<<12)	/* B8ZS code violation */
#define		XP_ERR_PDV		(1<<13)	/* pulse density violation */
#define		XP_ERR_ES		(1<<14)	/* errored second */
#define		XP_ERR_FEBE		(1<<15)
		uint jalts;		/* consecutive jitter atten. limit trips */
		uint sess;		/* consecutive severely errored seconds */
		uint nses;		/* consecutive non-severely errored seconds */
		uint config;
	} status;			/* state structure */
	uint curr;			/* current history index */
	struct st stats[3];		/* current statistics, 1 sec, 1 min. 15 min. */
	struct st hist[96];		/* historical statistics */
} sp_t;

STATIC void xp_init_sp(struct cd *, struct sp *, uint8_t);
STATIC struct sp *xp_alloc_sp(struct cd *, uint8_t);
STATIC void xp_free_sp(struct sp *);
STATIC struct sp *sp_get(struct sp *);
STATIC void sp_put(struct sp *);
STATIC struct sp *sp_find(uint);

typedef struct sg {
	atomic_t refcnt;		/* reference count */
	spinlock_t lock;		/* structure lock */
	uint group;			/* synchronization group number */
	uint members;			/* number of members */
	uint ifgtype;			/* type of current sync source */
	struct cd *master;		/* current group master */
	struct cd *cd;			/* list of cards in this group */
} sg_t;

STATIC struct sg *xp_alloc_sg(void);
STATIC void xp_free_sg(struct sg *);
STATIC struct sg *sg_get(struct sg *);
STATIC void sg_put(struct sg *);
STATIC struct sg *sg_find(uint);

typedef struct cd {
	atomic_t refcnt;		/* reference count */
	spinlock_t lock;		/* structure lock */
	ulong xll_region;		/* Xilinx 32-bit memory region */
	ulong xll_length;		/* Xilinx 32-bit memory length */
	volatile uint32_t *xll;		/* Xilinx 32-bit memory map */
	ulong xlb_region;		/* Xilinx 8-bit memory region */
	ulong xlb_length;		/* Xilinx 8-bit memory lenght */
	volatile uint8_t *xlb;		/* Xilinx 8-bit memory map */
	ulong plx_region;		/* PLX 9030 memory region */
	ulong plx_length;		/* PLX 9030 memory length */
	volatile uint16_t *plx;		/* PLX 9030 memory map */
	volatile uint8_t synreg;	/* local copy of synreg contents */
	volatile uint8_t ctlreg;	/* local copy of ctlreg contents (static bits) */
	volatile uint8_t ledreg;	/* local copy of ledreg contents */
	volatile uint8_t tstreg;	/* local copy of tstreg contents */
	volatile uint8_t clkreg;	/* local copy of need for E1DIV for local clock */
	uint frame;			/* frame number */
	struct sp *spans[4];		/* structures for spans */
	struct xp *xray;		/* xraying streams for this span */
	uint32_t *wbuf;			/* wr buffer */
	uint32_t *rbuf;			/* rd buffer */
	volatile int uebno;		/* upper elastic buffer number */
	volatile int lebno;		/* lower elastic buffer number */
	volatile int eval_syncsrc;	/* need to reevaluate sync src */
	volatile int leds;		/* leds on the card */
	int card;			/* index (card) */
	int board;			/* board hardware index */
	int device;			/* device hardware index */
	int devrev;			/* device hardware revision */
	int hw_flags;			/* board and device hardware flags */
	int ports;			/* number of ports populated on the board */
	struct {
		struct cd *next;	/* next card in this sync group */
		struct cd **prev;	/* prev card in this sync group */
		uint index;		/* index of this card in this sync group */
		struct sg *sg;		/* the synchronization group structure */
	} sg;
	ulong irq;			/* card irq */
	ulong bus;			/* card pci bus number */
	ulong slot;			/* card pci slot number */
	ulong iobase;			/* card iobase */
	struct tasklet_struct tasklet;	/* card tasklet */
#ifdef HAVE_KTYPE_IRQ_HANDLER_T
	irq_handler_t isr;		/* interrupt service routine */
#else
	 irqreturn_t(*isr) (int, void *, struct pt_regs *);	/* interrupt service routine */
#endif
	sdl_config_t config;		/* card configuration */
} cd_t;

STATIC struct cd *xp_alloc_cd(void);
STATIC void xp_free_cd(struct cd *);
STATIC struct cd *cd_get(struct cd *);
STATIC void cd_put(struct cd *);
STATIC struct cd *cd_find(uint);

#ifdef _MPS_SOURCE
#define ss7_fast_allocb(__bufpoolp, __size, __priority)	allocb(__size, __priority)
#define ss7_fast_freemsg(__bufpoolp, __mp) freemsg(__mp)
#define ss7_bufpool_reserve(__bufpoolp, __numb) do { } while (0)
#define ss7_bufpool_release(__bufpoolp, __numb) do { } while (0)
#define ss7_bufpool_init(__bufpoolp) do { } while (0)
#define ss7_bufpool_term(__bufpoolp) do { } while (0)
#else				/* _MPS_SOURCE */
STATIC struct ss7_bufpool xp_bufpool = { 0, };
#endif				/* _MPS_SOURCE */

#ifndef _OPTIMIZE_SPEED
noinline fastcall const char *
lmi_statename(lmi_long state)
{
	switch (state) {
	case LMI_UNATTACHED:
		return ("LMI_UNATTACHED");
	case LMI_ATTACH_PENDING:
		return ("LMI_ATTACH_PENDING");
	case LMI_UNUSABLE:
		return ("LMI_UNUSABLE");
	case LMI_DISABLED:
		return ("LMI_DISABLED");
	case LMI_ENABLE_PENDING:
		return ("LMI_ENABLE_PENDING");
	case LMI_ENABLED:
		return ("LMI_ENABLED");
	case LMI_DISABLE_PENDING:
		return ("LMI_DISABLE_PENDING");
	case LMI_DETACH_PENDING:
		return ("LMI_DETACH_PENDING");
	default:
		return ("(uknown)");
	}
}
#endif

/** xp_set_state: - set LMI state of private structure
  * @xp: private structure (locked) for which to set state
  * @state: the state to set
  */
static inline fastcall __hot void
xp_set_state(struct xp *xp, lmi_ulong state)
{
	if (likely(xp->i_state != state)) {
		LOGST(xp2sid(xp), "%s <- %s", lmi_statename(state), lmi_statename(xp->i_state));
		xp->i_state = state;
	}
}

/** xp_get_state: - get LMI state of private structure
  * @xp: private structure (locked) for which to get state
  * @state: the state to get
  */
static inline fastcall __hot lmi_ulong
xp_get_state(struct xp *xp)
{
	return (xp->i_state);
}

static inline fastcall __hot void
xp_save_state(struct xp *xp)
{
	xp->i_oldstate = xp->i_state;
}

static inline fastcall __hot void
xp_restore_state(struct xp *xp)
{
	xp_set_state(xp, xp->i_oldstate);
}

#define dl_get_state(xp) xp_get_state(xp)

#ifndef _OPTIMIZE_SPEED
noinline fastcall const char *
dl_statename(dl_ulong state)
{
	switch (state) {
	case DL_UNATTACHED:
		return ("DL_UNATTACHED");
	case DL_ATTACH_PENDING:
		return ("DL_ATTACH_PENDING");
	case DL_DETACH_PENDING:
		return ("DL_DETACH_PENDING");
	case DL_UNBOUND:
		return ("DL_UNBOUND");
	case DL_BIND_PENDING:
		return ("DL_BIND_PENDING");
	case DL_UNBIND_PENDING:
		return ("DL_UNBIND_PENDING");
	case DL_IDLE:
		return ("DL_IDLE");
	case DL_UDQOS_PENDING:
		return ("DL_UDQOS_PENDING");
	case DL_OUTCON_PENDING:
		return ("DL_OUTCON_PENDING");
	case DL_INCON_PENDING:
		return ("DL_INCON_PENDING");
	case DL_CONN_RES_PENDING:
		return ("DL_CONN_RES_PENDING");
	case DL_DATAXFER:
		return ("DL_DATAXFER");
	case DL_USER_RESET_PENDING:
		return ("DL_USER_RESET_PENDING");
	case DL_PROV_RESET_PENDING:
		return ("DL_PROV_RESET_PENDING");
	case DL_RESET_RES_PENDING:
		return ("DL_RESET_RES_PENDING");
	case DL_DISCON8_PENDING:
		return ("DL_DISCON8_PENDING");
	case DL_DISCON9_PENDING:
		return ("DL_DISCON9_PENDING");
	case DL_DISCON11_PENDING:
		return ("DL_DISCON11_PENDING");
	case DL_DISCON12_PENDING:
		return ("DL_DISCON12_PENDING");
	case DL_DISCON13_PENDING:
		return ("DL_DISCON13_PENDING");
	case DL_SUBS_BIND_PND:
		return ("DL_SUBS_BIND_PND");
	case DL_SUBS_UNBIND_PND:
		return ("DL_SUBS_UNBIND_PND");
	default:
		return ("(unknown)");
	}
}
#endif

/** dl_set_state: - set DLPI state of private structure
  * @xp: private structure (locked) for which to set state
  * @state: the state to set
  */
static inline fastcall __hot void
dl_set_state(struct xp *xp, dl_ulong state)
{
	if (likely(xp->i_state != state)) {
		LOGST(xp2sid(xp), "%s <- %s", dl_statename(state), dl_statename(xp->i_state));
		xp->i_state = state;
	}
}

#if	defined DEFINE_RWLOCK
static DEFINE_RWLOCK(xp_core_lock);
static DEFINE_RWLOCK(xp_list_lock);
#elif	defined __RW_LOCK_UNLOCKED
static rwlock_t xp_core_lock = __RW_LOCK_UNLOCKED(xp_core_lock);	/* protects ch->sp linkage */
static rwlock_t xp_list_lock = __RW_LOCK_UNLOCKED(xp_list_lock);	/* protects open list */
#elif	defined RW_LOCK_UNLOCKED
static rwlock_t xp_core_lock = RW_LOCK_UNLOCKED;	/* protect cd,sp,ch linkage */
static rwlock_t xp_list_lock = RW_LOCK_UNLOCKED;	/* protect open list */
#else
#error cannot initialize read-write locks
#endif

/*
 *  ------------------------------------------------------------------------
 *
 *  Card Structures and Macros
 *
 *  ------------------------------------------------------------------------
 */

#ifdef X400P_DOWNLOAD_FIRMWARE

#define GPIOC		(0x54 >> 1)	/* GPIO control register */
#define GPIO_WRITE	0x4000	/* GPIO4 data */
#define GPIO_PROGRAM	0x20000	/* GPIO5 data */
#define GPIO_INIT	0x100000	/* GPIO6 data */
#define GPIO_DONE	0x800000	/* GPIO7 data */
#endif

#define INTCSR (0x4c >> 1)
#define PLX_INTENA 0x43

#define STAREG	0x400
#define INTENABLED	(1<<0)	/* STAREG.0: interrupt enabled */
#define INTACTIVE	(1<<1)	/* STAREG.1: interrupt active */
#define DINTACT		(1<<2)	/* STAREG.2: dallas interrupt active */

#define SYNREG	0x400
#define SYNCSELF	0	/* SYNREG: 0x00 = sync to local clock (free run) */
#define SYNC1		1	/* SYNREG: 0x01 = sync source span 1 */
#define SYNC2		2	/* SYNREG: 0x02 = sync source span 2 */
#define SYNC3		3	/* SYNREG: 0x03 = sync source span 3 */
#define SYNC4		4	/* SYNREG: 0x04 = sync source span 4 */
#define SYNCEXTERN	5	/* SYNREG: 0x05 = sync to timing bus */
#define SYNCAUTO	6	/* SYNREG: 0x06 = sync to local clock (free run) */

#define CTLREG	0x401
#define INTENA		(1<<0)	/* CTLREG.0: interrupt enable */
#define OUTBIT		(1<<1)	/* CTLREG.1: drives "TEST1" signal ("Interrupt" outbit) */
#define DINTENA		(1<<2)	/* CTLREG.2: dallas interrupt enable (allows DINT signal to drive INT) */
#define MASTER		(1<<3)	/* CTLREG.3: external syncrhonization enable (MASTER signal). */
#define E1DIV		(1<<4)	/* CTLREG.4: select E1 divisor mode (0 to T1, 1 for E1). */
#define RSERLB		(1<<5)	/* CTLREG.5: remote serial loopback (TSER from RSER). */
#define LSERLB		(1<<6)	/* CTLREG.6: local serial loopback (Rx buffers from Tx buffers). */
#define INTACK		(1<<7)	/* CTLREG.7: interrupt acknowledge (set to 1 to acknowledge interrupt) */

#define LEDREG	0x402
/* LEDREG.0: span 1 green */
/* LEDREG.1: span 1 red */
#define LEDBLK 0
/* LEDREG.2: span 2 green */
/* LEDREG.3: span 2 red */
#define LEDGRN 1
/* LEDREG.4: span 3 green */
/* LEDREG.5: span 3 red */
#define LEDRED 2
/* LEDREG.6: span 4 green */
/* LEDREG.7: span 1 red */
#define LEDYEL 3

#define TSTREG	0x403
#define TEST2		(1<<0)	/* TSTREG.0: drives TEST2 pin */

#define CTLREG1	0x404
#define NONREVA		(1<<0)	/* CTLREG1.0: non-REV.A mode (set this bit for Dallas chips later than Rev.  A) */

#define X400_ABIT 8
#define X400_BBIT 4

#define X400_CARDS 16		/* 16 cards per system */
#define X400_SYNCS 16		/* 16 sync groups per system */
#define X400_SPANS 4		/* 4 spans per card */

/* allow alarms to settle for 5 seconds */
#define X400P_SDL_ALARM_SETTLE_SECONDS	    5
#define X400P_SDL_ALARM_SETTLE_E1	    80
#define X400P_SDL_ALARM_SETTLE_T1	    120
#define X400P_SDL_ALARM_SETTLE_TIME	    5000

enum xp_board {
	PLX9030 = 0,
	PLXDEVBRD,
	X400P,
	E400P,
	T400P,
	X400PSS7,
	E400PSS7,
	T400PSS7,
	V400P,
	V400PE,
	V400PT,
	V401PE,
	V401PT,
	AE400P,
	AT400P,
	A400P,
	A400PE,
	A400PT,
	CP100,
	CP100P,
	CP100E,
	CP200,
	CP200P,
	CP200E,
	CP400,
	CP400P,
	CP400E
};

/* indexed by xp_board above */

#ifndef __devinitdata
#define __devinitdata __initdata
#endif

#ifndef __devinit
#define __devinit __init
#endif

#ifndef __devexit
#define __devexit __exit
#endif

#ifndef __devexit_p
#define __devexit_p __exit_p
#endif

/* *INDENT-OFF* */
static struct {
	char *name;
	uint32_t hw_flags;
	uint32_t idle_word;
	int ports;
} xp_board_info[] __devinitdata = {
	{ "PLX 9030",			0, 0x00000000, 4 },
	{ "PLX Development Board",	0, 0x00000000, 4 },
	{ "X400P",			1, 0xffffffff, 4 },
	{ "E400P",			1, 0xffffffff, 4 },
	{ "T400P",			1, 0xfefefefe, 4 },
	{ "X400P-SS7",			1, 0xffffffff, 4 },
	{ "E400P-SS7",			1, 0xffffffff, 4 },
	{ "T400P-SS7",			1, 0xfefefefe, 4 },
	{ "V400P",			1, 0xffffffff, 4 },
	{ "V400PE",			1, 0xffffffff, 4 },
	{ "V400PT",			1, 0xfefefefe, 4 },
	{ "V401PE",			1, 0xffffffff, 4 },
	{ "V401PT",			1, 0xfefefefe, 4 },
	{ "AE400P",			1, 0xffffffff, 4 },
	{ "AT400P",			1, 0xfefefefe, 4 },
	{ "A400P",			1, 0xffffffff, 4 },
	{ "A400PE",			1, 0xffffffff, 4 },
	{ "A400PT",			1, 0xfefefefe, 4 },
	{ "CP100",			1, 0xffffffff, 1 },
	{ "CP100P",			1, 0xffffffff, 1 },
	{ "CP100E",			1, 0xffffffff, 1 },
	{ "CP200",			1, 0xffffffff, 2 },
	{ "CP200P",			1, 0xffffffff, 2 },
	{ "CP200E",			1, 0xffffffff, 2 },
	{ "CP400",			1, 0xffffffff, 4 },
	{ "CP400P",			1, 0xffffffff, 4 },
	{ "CP400E",			1, 0xffffffff, 4 }
};

#define XPF_SUPPORTED	(1<<0)

#define XP_DEV_IDMASK	0xf0
#define XP_DEV_SHIFT	4
#define XP_DEV_REVMASK	0x0f
#define XP_DEV_DS2152	0x00	/* '0000XXXX'B DS2152  */
#define XP_DEV_DS21352	0x01	/* '0001XXXX'B DS21352 */
#define XP_DEV_DS21552	0x02	/* '0010XXXX'B DS21552 */
#define XP_DEV_DS21458	0x08	/* '1000XXXX'B DS21458 */
#define XP_DEV_DS21354	0x09	/* '1001XXXX'B DS21354 */
#define XP_DEV_DS21554	0x0a	/* '1010XXXX'B DS21554 */
#define XP_DEV_DS2155	0x0b	/* '1011XXXX'B DS2155  */
#define XP_DEV_DS21455	0x0c	/* '1100XXXX'B DS21455 */

#define XP_DEV_DS2153	0x10	/* '00000000'B DS2153  */ /* make this 0x10: it really conflicts with 0x00 */
#define XP_DEV_DS2156	0x1c	/* '1010XXXX'B DS2156  */ /* make this 0x1c: it really conflicts with 0x0c */
#define XP_DEV_DS2154	0x18	/* '1000XXXX'B DS2154  */ /* make this 0x18: it really conflicts with 0x08 */

STATIC struct {
	char *name;
	uint32_t hw_flags;
} xp_device_info[] __devinitdata = {
	{ "DS2152 (T1)",	0 },	/* { "DS2153 (E1)", 0 }, */
	{ "DS21352 (T1)",	1 },
	{ "DS21552 (T1)",	1 },
	{ "Unknown ID 0011",	0 },
	{ "Unknown ID 0100",	0 },
	{ "Unknown ID 0101",	0 },
	{ "Unknown ID 0110",	0 },
	{ "Unknown ID 0111",	0 },
	{ "DS21458 (E1/T1/J1)", 1 },	/* { "DS2154 (E1)", 0 }, */
	{ "DS21354 (E1)",	1 },
	{ "DS21554 (E1)",	1 },
	{ "DS2155 (E1/T1/J1)",	1 },
	{ "DS21455 (E1/T1/J1)",	1 },	/* { "DS2156 (E1/T1/J1)", 1 }, */
	{ "Unknown ID 1101",	0 },
	{ "Unknown ID 1110",	0 },
	{ "Unknown ID 1111",	0 }
};

STATIC struct pci_device_id xp_pci_tbl[] __devinitdata = {
	{PCI_VENDOR_ID_PLX, 0x9030, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, PLX9030},
	{PCI_VENDOR_ID_PLX, 0x3001, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, PLXDEVBRD},
	{PCI_VENDOR_ID_PLX, 0xD00D, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, X400P},
	{PCI_VENDOR_ID_PLX, 0x0557, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, X400PSS7},
	{PCI_VENDOR_ID_PLX, 0xD44D, PCI_ANY_ID, 0x17F6,     PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, CP100},
	{PCI_VENDOR_ID_PLX, 0xD44D, PCI_ANY_ID, 0x17F8,     PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, CP200},
	{PCI_VENDOR_ID_PLX, 0xD44D, PCI_ANY_ID, 0x17F7,     PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, CP400},
	{PCI_VENDOR_ID_PLX, 0x4000, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, V400P},
	{PCI_VENDOR_ID_PLX, 0xD33D, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, V401PT},
	{PCI_VENDOR_ID_PLX, 0xD44D, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, V401PE},
	{PCI_VENDOR_ID_PLX, 0x1000, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, AT400P},
	{PCI_VENDOR_ID_PLX, 0x2000, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, AE400P},
	{PCI_VENDOR_ID_PLX, 0x4001, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, A400PE},
	{PCI_VENDOR_ID_PLX, 0x4002, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_OTHER << 8, 0xffff00, A400PT},
	{0,}
};
/* *INDENT-ON* */

#ifdef MODULE_DEVICE_TABLE
MODULE_DEVICE_TABLE(pci, xp_pci_tbl);
#ifdef MODULE_ALIAS
MODULE_ALIAS("pci:v000010B5d00009030sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00003001sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d0000D00Dsv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00000557sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00004000sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d0000D33Dsv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d0000D44Dsv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00001000sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00002000sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00004001sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d00004002sv*sd*bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d0000D44Dsv*sd000017F6bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d0000D44Dsv*sd000017F8bc06sc80i*");
MODULE_ALIAS("pci:v000010B5d0000D44Dsv*sd000017F7bc06sc80i*");
#endif				/* MODULE_ALIAS */
#endif				/* MODULE_DEVICE_TABLE */

unsigned short ansi = 0;
unsigned short etsi = 0;
unsigned short japan = 0;

#ifndef module_param
MODULE_PARM(ansi, "h");
MODULE_PARM(etsi, "h");
MODULE_PARM(japan, "h");
#else
module_param(ansi, ushort, 0444);
module_param(etsi, ushort, 0444);
module_param(japan, ushort, 0444);
#endif
MODULE_PARM_DESC(japan, "Configure any T1/J1 or E1/T1/J1 devices for J1 (only) operation (overrides ansi=1).");
MODULE_PARM_DESC(ansi, "Configure any T1/J1 or E1/T1/J1 devices for T1 (only) operation.");
MODULE_PARM_DESC(etsi, "Configure all E1 or E1/T1/J1 devices for E1 (only) operation (overrides ansi=1).");

/* Map from T1 channel number to Tormenta backpane time slot offset. */
STATIC int xp_t1_chan_map[] = {
	0,
	1, 2, 3,
	5, 6, 7,
	9, 10, 11,
	13, 14, 15,
	17, 18, 19,
	21, 22, 23,
	25, 26, 27,
	29, 30, 31
};

/* Map from E1 channel number to Tormenta backplane time slot offset. */
STATIC int xp_e1_chan_map[] = {
	0,
	1, 2, 3, 4,
	5, 6, 7, 8,
	9, 10, 11, 12,
	13, 14, 15, 16,
	17, 18, 19, 20,
	21, 22, 23, 24,
	25, 26, 27, 28,
	29, 30, 31
};

#define XP_T1_TS_VALID_MASK	0x00ffffff	/* Mask of valid T1 time slots. */
#define XP_E1_TS_VALID_MASK	0x7fffffff	/* Mask of valid E1 time slots. */
#define XP_T1_CHAN_VALID_MASK	0xeeeeeeee	/* Mask of valid T1 Tormenta channels. */
#define XP_E1_CHAN_VALID_MASK	0xfffffffe	/* Mask of valid E1 Tormenta channels. */

#ifdef __LITTLE_ENDIAN
#define span_to_byte(__span) (3-(__span))
#else
#ifdef __BIG_ENDIAN
#define span_to_byte(__span) (__span)
#else
#error "Must know the endianess of processor\n"
#endif
#endif

STATIC int x400p_boards = 0;
STATIC struct cd *x400p_cards[X400_CARDS] = { NULL, };	/* master card list */

STATIC int x400p_groups = 0;
STATIC struct sg *x400p_syncs[X400_SYNCS] = { NULL, };	/* master sync list */

#if	defined DEFINE_RWLOCK
STATIC DEFINE_RWLOCK(x400p_lock);
#elif	defined __RW_LOCK_UNLOCKED
STATIC rwlock_t x400p_lock = __RW_LOCK_UNLOCKED(x400p_lock);
#elif	defined RW_LOCK_UNLOCKED
STATIC rwlock_t x400p_lock = RW_LOCK_UNLOCKED;
#else
#error cannot initialize read-write locks
#endif
STATIC struct xp *x400p_xrays;		/* xraying streams for this driver */

#define X400P_EBUFNO (1<<7)	/* 128k elastic buffers */

/*
 *  ========================================================================
 *
 *  PRIMITIVES Sent Upstream
 *
 *  ========================================================================
 */

static inline fastcall __hot void
snapmsg(mblk_t *mp, ssize_t len)
{
	register mblk_t *b;
	register ssize_t remain = len;

	for (b = mp; b; b = b->b_cont) {
		register ssize_t blen = b->b_rptr - b->b_wptr;

		if (blen <= 0)
			continue;
		if (unlikely(remain == 0)) {
			b->b_wptr = b->b_rptr;
		} else if (likely(blen <= remain)) {
			remain -= blen;
		} else {
			b->b_wptr = b->b_rptr + remain;
			remain = 0;
		}
	}

}

noinline fastcall __hot void
xp_dup_to_xray(struct xp *xp, mblk_t *mp, uint mask, size_t hlen, struct timeval *tv, size_t mlen)
{
	for (; xp; xp = xp->xray.next) {
		mblk_t *dp;
		uint hlen = 0;
		uint flags = xp->xray.nit.flags;

		if (!(flags & mask))
			continue;
		if (unlikely(!canputnext(xp->rq)))
			goto dropped;
		if (unlikely((dp = dupmsg(mp)) == NULL))
			goto dropped;
		if (unlikely(flags & NI_TIMESTAMP))
			hlen += sizeof(*tv);
		if (unlikely(flags & NI_DROPS))
			hlen += sizeof(ulong);
		if (unlikely(flags & NI_LEN))
			hlen += sizeof(ulong);
		if (unlikely(xp->xray.nit.snap > 0))
			snapmsg(dp, xp->xray.nit.snap + hlen);
		if (unlikely(hlen != 0)) {
			mblk_t *bp = allocb(hlen, BPRI_MED);

			/* nit(4) says theses must be in their own M_PROTO */
			DB_TYPE(bp) = M_PROTO;
			if (unlikely(bp == NULL)) {
				freemsg(dp);
				goto dropped;
			}
			bp->b_wptr = bp->b_rptr + hlen;
			bp->b_cont = dp;
			if (likely(flags & NI_LEN)) {
				bp->b_wptr -= sizeof(ulong);
				*(ulong *) bp->b_wptr = mlen;
			}
			if (likely(flags & NI_DROPS)) {
				bp->b_wptr -= sizeof(ulong);
				*(ulong *) bp->b_wptr = atomic_read(&xp->xray.drops);
				atomic_set(&xp->xray.drops, 0);
			}
			if (likely(flags & NI_TIMESTAMP)) {
				bp->b_wptr -= sizeof(*tv);
				*(typeof(tv)) bp->b_wptr = *tv;
			}
			bp->b_wptr = bp->b_rptr + hlen;
			dp = bp;
		}
		putnext(xp->rq, dp);
		continue;
	      dropped:
		atomic_inc(&xp->xray.drops);
	}
}

noinline fastcall __hot void
xp_xray_data(struct ch *ch, mblk_t *mp, uint flags, size_t hlen)
{
	struct sp *sp = ch->sp;
	struct timeval tv = { 0, 0 };
	size_t mlen = 0;

	prefetch(sp);
	if (unlikely(flags & NI_TIMESTAMP))
		/* some xray stream wants a timestamp */
		do_gettimeofday(&tv);
	if (unlikely(flags & NI_LEN))
		/* some xray stream wants message length */
		mlen = msgsize(mp);

	if (unlikely(flags & XP_XRAY_CHAN))
		xp_dup_to_xray(ch->xray.list, mp, flags, hlen, &tv, mlen);
	{
		struct cd *cd = sp->cd;

		prefetch(cd);
		if (unlikely(flags & XP_XRAY_SPAN))
			xp_dup_to_xray(sp->xray, mp, flags, hlen, &tv, mlen);
		prefetch(x400p_xrays);
		if (unlikely(flags & XP_XRAY_CARD))
			xp_dup_to_xray(cd->xray, mp, flags, hlen, &tv, mlen);
	}
	if (unlikely(flags & XP_XRAY_GLOB))
		xp_dup_to_xray(x400p_xrays, mp, flags, hlen, &tv, mlen);
}

/* The Old SNIT headers are struct nit_iftime (2 ulongs) if NI_TIMESTAMP set, struct nit_ifdrops (1
 * ulong) if NI_DROPS set, struct nit_iflen (1 ulong) if NI_LEN set.  This is up to 4 ulongs (or as
 * many as 32 bytes). */

/* SL data is DLT_MTP3, mp must be M_DATA */
static inline fastcall __hot void
sl_xray_data_rx(struct ch *ch, mblk_t *mp)
{
	if (unlikely(ch->xray.flags & XP_XRAY_SL_RX))
		xp_xray_data(ch, mp, ch->xray.flags & (XP_XRAY_SL_RX | NI_USERBITS), 0);
}

/* SL data is DLT_MTP3, mp must be M_DATA */
static inline fastcall __hot void
sl_xray_data_tx(struct ch *ch, mblk_t *mp)
{
	if (unlikely(ch->xray.flags & XP_XRAY_SL_TX))
		xp_xray_data(ch, mp, ch->xray.flags & (XP_XRAY_SL_TX | NI_USERBITS), 0);
}

/** sdt_xray_data: - generate SDT-level monitoring data
  * @ch: channel structure (locked)
  * @mp: the M_DATA buffer containing the data
  * @flags: xray flags
  * @dir: the direction of the data
  *
  * pseudo header is 1 byte for sent or received, true when sent, 1 byte for Annex A used, true when
  * used, and 2 bytes for the link number.  We used the ppa for the link number.  For RX data, we
  * can fix the lower layer to leave 4 bytes room on the head of its buffers so that we do not have
  * to allocate a message block. Note that the upper layers do not add headers so even though the
  * message blocks are shared, the pseudo header will not later be overwritten.
  *
  * Note that SS7_POPT_XSN is tested instead of SS7_POPT_HSL. HSL is just for high-speed links (full
  * span).  XSN is for extended sequence numbers, the only thing PCAP worries about.
  */
noinline fastcall __hot void
sdt_xray_data(struct ch *ch, mblk_t *mp, uint flags, uint dir)
{
	mblk_t *dp = mp;

	if (likely(DB_LIM(mp) + 4 < mp->b_rptr)) {
		dp->b_rptr -= 4;
	} else {
		if (unlikely((dp = allocb(4, BPRI_MED)) == NULL))
			return;
		dp->b_wptr = dp->b_rptr + 4;
		dp->b_cont = mp;
	}
	dp->b_rptr[0] = dir;
	dp->b_rptr[1] = (ch->option.popt & SS7_POPT_XSN) ? 1 : 0;
	*(uint16_t *) &dp->b_rptr[2] = ch->ppa;

	xp_xray_data(ch, dp, ch->xray.flags & (flags | NI_USERBITS), 4);
	if (dp == mp)
		dp->b_rptr += 4;
	else
		freeb(dp);
}

/* SDT data is DLT_MTP2_WITH_PHDR, mp must be M_DATA */
static inline fastcall __hot void
sdt_xray_data_rx(struct ch *ch, mblk_t *mp)
{
	if (unlikely(ch->xray.flags & XP_XRAY_SDT_RX))
		sdt_xray_data(ch, mp, XP_XRAY_SDT_RX, 0);
}

/* SDT data is DLT_MTP2_WITH_PHDR, mp must be M_DATA */
static inline fastcall __hot void
sdt_xray_data_tx(struct ch *ch, mblk_t *mp)
{
	if (unlikely(ch->xray.flags & XP_XRAY_SDT_TX))
		sdt_xray_data(ch, mp, XP_XRAY_SDT_TX, 1);
}

/* PCAP can't capture raw bit streams */
static inline __hot void
sdl_xray_data_rx(struct ch *ch, mblk_t *mp)
{
	if (unlikely(ch->xray.flags & XP_XRAY_SDL_RX))
		xp_xray_data(ch, mp, ch->xray.flags & XP_XRAY_SDL_RX, 0);
}

static inline __hot void
sdl_xray_data_tx(struct ch *ch, mblk_t *mp)
{
	if (unlikely(ch->xray.flags & XP_XRAY_SDL_TX))
		xp_xray_data(ch, mp, ch->xray.flags & XP_XRAY_SDL_TX, 0);
}

/*
 * =======================================================================
 *
 *  PRIMITIVES
 *
 * =======================================================================
 */

#if !defined _OPTIMIZE_SPEED
/** lmi_primname: - name LMI, SL, SDT or SDL primitives.
  * @prim: the primitive to name
  * Returns the name of the primitive or "(unknown)".
  */
static const char *
sl_primname(lmi_long prim)
{
	switch (prim) {
	case SL_PDU_REQ:
		return ("SL_PDU_REQ");
	case SL_EMERGENCY_REQ:
		return ("SL_EMERGENCY_REQ");
	case SL_EMERGENCY_CEASES_REQ:
		return ("SL_EMERGENCY_CEASES_REQ");
	case SL_START_REQ:
		return ("SL_START_REQ");
	case SL_STOP_REQ:
		return ("SL_STOP_REQ");
	case SL_RETRIEVE_BSNT_REQ:
		return ("SL_RETRIEVE_BSNT_REQ");
	case SL_RETRIEVAL_REQUEST_AND_FSNC_REQ:
		return ("SL_RETRIEVAL_REQUEST_AND_FSNC_REQ");
	case SL_CLEAR_BUFFERS_REQ:
		return ("SL_CLEAR_BUFFERS_REQ");
	case SL_CLEAR_RTB_REQ:
		return ("SL_CLEAR_RTB_REQ");
	case SL_CONTINUE_REQ:
		return ("SL_CONTINUE_REQ");
	case SL_LOCAL_PROCESSOR_OUTAGE_REQ:
		return ("SL_LOCAL_PROCESSOR_OUTAGE_REQ");
	case SL_RESUME_REQ:
		return ("SL_RESUME_REQ");
	case SL_CONGESTION_DISCARD_REQ:
		return ("SL_CONGESTION_DISCARD_REQ");
	case SL_CONGESTION_ACCEPT_REQ:
		return ("SL_CONGESTION_ACCEPT_REQ");
	case SL_NO_CONGESTION_REQ:
		return ("SL_NO_CONGESTION_REQ");
	case SL_POWER_ON_REQ:
		return ("SL_POWER_ON_REQ");
	case SL_OPTMGMT_REQ:
		return ("SL_OPTMGMT_REQ");
	case SL_NOTIFY_REQ:
		return ("SL_NOTIFY_REQ");
	case SL_PDU_IND:
		return ("SL_PDU_IND");
	case SL_LINK_CONGESTED_IND:
		return ("SL_LINK_CONGESTED_IND");
	case SL_LINK_CONGESTION_CEASED_IND:
		return ("SL_LINK_CONGESTION_CEASED_IND");
	case SL_RETRIEVED_MESSAGE_IND:
		return ("SL_RETRIEVED_MESSAGE_IND");
	case SL_RETRIEVAL_COMPLETE_IND:
		return ("SL_RETRIEVAL_COMPLETE_IND");
	case SL_RB_CLEARED_IND:
		return ("SL_RB_CLEARED_IND");
	case SL_BSNT_IND:
		return ("SL_BSNT_IND");
	case SL_IN_SERVICE_IND:
		return ("SL_IN_SERVICE_IND");
	case SL_OUT_OF_SERVICE_IND:
		return ("SL_OUT_OF_SERVICE_IND");
	case SL_REMOTE_PROCESSOR_OUTAGE_IND:
		return ("SL_REMOTE_PROCESSOR_OUTAGE_IND");
	case SL_REMOTE_PROCESSOR_RECOVERED_IND:
		return ("SL_REMOTE_PROCESSOR_RECOVERED_IND");
	case SL_RTB_CLEARED_IND:
		return ("SL_RTB_CLEARED_IND");
	case SL_RETRIEVAL_NOT_POSSIBLE_IND:
		return ("SL_RETRIEVAL_NOT_POSSIBLE_IND");
	case SL_BSNT_NOT_RETRIEVABLE_IND:
		return ("SL_BSNT_NOT_RETRIEVABLE_IND");
	case SL_OPTMGMT_ACK:
		return ("SL_OPTMGMT_ACK");
	case SL_NOTIFY_IND:
		return ("SL_NOTIFY_IND");
	case SL_LOCAL_PROCESSOR_OUTAGE_IND:
		return ("SL_LOCAL_PROCESSOR_OUTAGE_IND");
	case SL_LOCAL_PROCESSOR_RECOVERED_IND:
		return ("SL_LOCAL_PROCESSOR_RECOVERED_IND");
	default:
		return ("(unknown)");
	}
}

static const char *
sdt_primname(const lmi_long prim)
{
	switch (prim) {
	case SDT_DAEDT_TRANSMISSION_REQ:
		return ("SDT_DAEDT_TRANSMISSION_REQ");
	case SDT_DAEDT_START_REQ:
		return ("SDT_DAEDT_START_REQ");
	case SDT_DAEDR_START_REQ:
		return ("SDT_DAEDR_START_REQ");
	case SDT_AERM_START_REQ:
		return ("SDT_AERM_START_REQ");
	case SDT_AERM_STOP_REQ:
		return ("SDT_AERM_STOP_REQ");
	case SDT_AERM_SET_TI_TO_TIN_REQ:
		return ("SDT_AERM_SET_TI_TO_TIN_REQ");
	case SDT_AERM_SET_TI_TO_TIE_REQ:
		return ("SDT_AERM_SET_TI_TO_TIE_REQ");
	case SDT_SUERM_START_REQ:
		return ("SDT_SUERM_START_REQ");
	case SDT_SUERM_STOP_REQ:
		return ("SDT_SUERM_STOP_REQ");
	case SDT_RC_SIGNAL_UNIT_IND:
		return ("SDT_RC_SIGNAL_UNIT_IND");
	case SDT_RC_CONGESTION_ACCEPT_IND:
		return ("SDT_RC_CONGESTION_ACCEPT_IND");
	case SDT_RC_CONGESTION_DISCARD_IND:
		return ("SDT_RC_CONGESTION_DISCARD_IND");
	case SDT_RC_NO_CONGESTION_IND:
		return ("SDT_RC_NO_CONGESTION_IND");
	case SDT_IAC_CORRECT_SU_IND:
		return ("SDT_IAC_CORRECT_SU_IND");
	case SDT_IAC_ABORT_PROVING_IND:
		return ("SDT_IAC_ABORT_PROVING_IND");
	case SDT_LSC_LINK_FAILURE_IND:
		return ("SDT_LSC_LINK_FAILURE_IND");
	case SDT_TXC_TRANSMISSION_REQUEST_IND:
		return ("SDT_TXC_TRANSMISSION_REQUEST_IND");
	default:
		return ("(unknown)");
	}
}

static const char *
sdl_primname(lmi_long prim)
{
	switch (prim) {
	case SDL_BITS_FOR_TRANSMISSION_REQ:
		return ("SDL_BITS_FOR_TRANSMISSION_REQ");
	case SDL_CONNECT_REQ:
		return ("SDL_CONNECT_REQ");
	case SDL_DISCONNECT_REQ:
		return ("SDL_DISCONNECT_REQ");
	case SDL_RECEIVED_BITS_IND:
		return ("SDL_RECEIVED_BITS_IND");
	case SDL_DISCONNECT_IND:
		return ("SDL_DISCONNECT_IND");
	default:
		return ("(unknown)");
	}
}

static const char *
lmi_primname(lmi_long prim)
{
	switch (prim) {
	case LMI_INFO_REQ:
		return ("LMI_INFO_REQ");
	case LMI_ATTACH_REQ:
		return ("LMI_ATTACH_REQ");
	case LMI_DETACH_REQ:
		return ("LMI_DETACH_REQ");
	case LMI_ENABLE_REQ:
		return ("LMI_ENABLE_REQ");
	case LMI_DISABLE_REQ:
		return ("LMI_DISABLE_REQ");
	case LMI_OPTMGMT_REQ:
		return ("LMI_OPTMGMT_REQ");
	case LMI_INFO_ACK:
		return ("LMI_INFO_ACK");
	case LMI_OK_ACK:
		return ("LMI_OK_ACK");
	case LMI_ERROR_ACK:
		return ("LMI_ERROR_ACK");
	case LMI_ENABLE_CON:
		return ("LMI_ENABLE_CON");
	case LMI_DISABLE_CON:
		return ("LMI_DISABLE_CON");
	case LMI_OPTMGMT_ACK:
		return ("LMI_OPTMGMT_ACK");
	case LMI_ERROR_IND:
		return ("LMI_ERROR_IND");
	case LMI_STATS_IND:
		return ("LMI_STATS_IND");
	case LMI_EVENT_IND:
		return ("LMI_EVENT_IND");
	default:
		return ("(unknown)");
	}
}

static const char *
dlpi_primname(dl_ulong prim)
{
	switch (prim) {
	case DL_INFO_REQ:
		return ("DL_INFO_REQ");
	case DL_INFO_ACK:
		return ("DL_INFO_ACK");
	case DL_ATTACH_REQ:
		return ("DL_ATTACH_REQ");
	case DL_DETACH_REQ:
		return ("DL_DETACH_REQ");
	case DL_BIND_REQ:
		return ("DL_BIND_REQ");
	case DL_BIND_ACK:
		return ("DL_BIND_ACK");
	case DL_UNBIND_REQ:
		return ("DL_UNBIND_REQ");
	case DL_OK_ACK:
		return ("DL_OK_ACK");
	case DL_ERROR_ACK:
		return ("DL_ERROR_ACK");
	case DL_SUBS_BIND_REQ:
		return ("DL_SUBS_BIND_REQ");
	case DL_SUBS_BIND_ACK:
		return ("DL_SUBS_BIND_ACK");
	case DL_SUBS_UNBIND_REQ:
		return ("DL_SUBS_UNBIND_REQ");
	case DL_ENABMULTI_REQ:
		return ("DL_ENABMULTI_REQ");
	case DL_DISABMULTI_REQ:
		return ("DL_DISABMULTI_REQ");
	case DL_PROMISCON_REQ:
		return ("DL_PROMISCON_REQ");
	case DL_PROMISCOFF_REQ:
		return ("DL_PROMISCOFF_REQ");
	case DL_UNITDATA_REQ:
		return ("DL_UNITDATA_REQ");
	case DL_UNITDATA_IND:
		return ("DL_UNITDATA_IND");
	case DL_UDERROR_IND:
		return ("DL_UDERROR_IND");
	case DL_UDQOS_REQ:
		return ("DL_UDQOS_REQ");
	case DL_CONNECT_REQ:
		return ("DL_CONNECT_REQ");
	case DL_CONNECT_IND:
		return ("DL_CONNECT_IND");
	case DL_CONNECT_RES:
		return ("DL_CONNECT_RES");
	case DL_CONNECT_CON:
		return ("DL_CONNECT_CON");
	case DL_TOKEN_REQ:
		return ("DL_TOKEN_REQ");
	case DL_TOKEN_ACK:
		return ("DL_TOKEN_ACK");
	case DL_DISCONNECT_REQ:
		return ("DL_DISCONNECT_REQ");
	case DL_DISCONNECT_IND:
		return ("DL_DISCONNECT_IND");
	case DL_RESET_REQ:
		return ("DL_RESET_REQ");
	case DL_RESET_IND:
		return ("DL_RESET_IND");
	case DL_RESET_RES:
		return ("DL_RESET_RES");
	case DL_RESET_CON:
		return ("DL_RESET_CON");
	case DL_DATA_ACK_REQ:
		return ("DL_DATA_ACK_REQ");
	case DL_DATA_ACK_IND:
		return ("DL_DATA_ACK_IND");
	case DL_DATA_ACK_STATUS_IND:
		return ("DL_DATA_ACK_STATUS_IND");
	case DL_REPLY_REQ:
		return ("DL_REPLY_REQ");
	case DL_REPLY_IND:
		return ("DL_REPLY_IND");
	case DL_REPLY_STATUS_IND:
		return ("DL_REPLY_STATUS_IND");
	case DL_REPLY_UPDATE_REQ:
		return ("DL_REPLY_UPDATE_REQ");
	case DL_REPLY_UPDATE_STATUS_IND:
		return ("DL_REPLY_UPDATE_STATUS_IND 0x28");
	case DL_XID_REQ:
		return ("DL_XID_REQ");
	case DL_XID_IND:
		return ("DL_XID_IND");
	case DL_XID_RES:
		return ("DL_XID_RES");
	case DL_XID_CON:
		return ("DL_XID_CON");
	case DL_TEST_REQ:
		return ("DL_TEST_REQ");
	case DL_TEST_IND:
		return ("DL_TEST_IND");
	case DL_TEST_RES:
		return ("DL_TEST_RES");
	case DL_TEST_CON:
		return ("DL_TEST_CON");
	case DL_PHYS_ADDR_REQ:
		return ("DL_PHYS_ADDR_REQ");
	case DL_PHYS_ADDR_ACK:
		return ("DL_PHYS_ADDR_ACK");
	case DL_SET_PHYS_ADDR_REQ:
		return ("DL_SET_PHYS_ADDR_REQ");
	case DL_GET_STATISTICS_REQ:
		return ("DL_GET_STATISTICS_REQ");
	case DL_GET_STATISTICS_ACK:
		return ("DL_GET_STATISTICS_ACK");
	case DL_MONITOR_LINK_LAYER:
		return ("DL_MONITOR_LINK_LAYER");
#ifdef _SUN_SOURCE
	case DL_NOTIFY_REQ:
		return ("DL_NOTIFY_REQ");
	case DL_NOTIFY_ACK:
		return ("DL_NOTIFY_ACK");
	case DL_NOTIFY_IND:
		return ("DL_NOTIFY_IND");
	case DL_AGGR_REQ:
		return ("DL_AGGR_REQ");
	case DL_AGGR_IND:
		return ("DL_AGGR_IND");
	case DL_UNAGGR_REQ:
		return ("DL_UNAGGR_REQ");
	case DL_CAPABILITY_REQ:
		return ("DL_CAPABILITY_REQ");
	case DL_CAPABILITY_ACK:
		return ("DL_CAPABILITY_ACK");
	case DL_CONTROL_REQ:
		return ("DL_CONTROL_REQ");
	case DL_CONTROL_ACK:
		return ("DL_CONTROL_ACK");
	case DL_PASSIVE_REQ:
		return ("DL_PASSIVE_REQ");
	case DL_INTR_MODE_REQ:
		return ("DL_INTR_MODE_REQ");
#endif				/* _SUN_SOURCE */
#ifdef _HPUX_SOURCE
	case DL_HP_PPA_REQ:
		return ("DL_HP_PPA_REQ");
	case DL_HP_PPA_ACK:
		return ("DL_HP_PPA_ACK");
	case DL_HP_MULTICAST_LIST_REQ:
		return ("DL_HP_MULTICAST_LIST_REQ");
	case DL_HP_MULTICAST_LIST_ACK:
		return ("DL_HP_MULTICAST_LIST_ACK");
	case DL_HP_RAWDATA_REQ:
		return ("DL_HP_RAWDATA_REQ");
	case DL_HP_RAWDATA_IND:
		return ("DL_HP_RAWDATA_IND");
	case DL_HP_HW_RESET_REQ:
		return ("DL_HP_HW_RESET_REQ");
	case DL_HP_INFO_REQ:
		return ("DL_HP_INFO_REQ");
	case DL_HP_INFO_ACK:
		return ("DL_HP_INFO_ACK");
	case DL_HP_SET_ACK_TO_REQ:
		return ("DL_HP_SET_ACK_TO_REQ");
	case DL_HP_SET_P_TO_REQ:
		return ("DL_HP_SET_P_TO_REQ");
	case DL_HP_SET_REJ_TO_REQ:
		return ("DL_HP_SET_REJ_TO_REQ");
	case DL_HP_SET_BUSY_TO_REQ:
		return ("DL_HP_SET_BUSY_TO_REQ");
	case DL_HP_SET_SEND_ACK_TO_REQ:
		return ("DL_HP_SET_SEND_ACK_TO_REQ");
	case DL_HP_SET_MAX_RETRIES_REQ:
		return ("DL_HP_SET_MAX_RETRIES_REQ");
	case DL_HP_SET_ACK_THRESHOLD_REQ:
		return ("DL_HP_SET_ACK_THRESHOLD_REQ");
	case DL_HP_SET_LOCAL_WIN_REQ:
		return ("DL_HP_SET_LOCAL_WIN_REQ");
	case DL_HP_SET_REMOTE_WIN_REQ:
		return ("DL_HP_SET_REMOTE_WIN_REQ");
	case DL_HP_CLEAR_STATS_REQ:
		return ("DL_HP_CLEAR_STATS_REQ");
	case DL_HP_SET_LOCAL_BUSY_REQ:
		return ("DL_HP_SET_LOCAL_BUSY_REQ");
	case DL_HP_CLEAR_LOCAL_BUSY_REQ:
		return ("DL_HP_CLEAR_LOCAL_BUSY_REQ");
#endif				/* _HPUX_SOURCE */
	default:
		return ("(unknown)");
	}
}
#endif				/* !defined _OPTIMIZE_SPEED */

/*
 *  STREAMS MESSAGES ISSUED UPSTREAM
 *  -------------------------------------------------------------------------
 */

/** m_error: issue M_ERROR message
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: message to reuse
  * @err: +'ve or -'ve error to return
  *
  * Note that this is only ever called in response to a message on the write queue.  The passed in
  * message is always consumed.
  */
noinline __unlikely int
m_error(struct xp *xp, queue_t *q, mblk_t *mp, int err)
{
	if (mp || (mp = mi_allocb(q, 2, BPRI_MED))) {
		if (mp->b_cont)
			freemsg(XCHG(&mp->b_cont, NULL));
		/* I don't know that the above was necessary, the Stream head will not look beyond the
		   M_ERROR message block. */
		mp->b_band = 0;
		DB_TYPE(mp) = M_ERROR;
		mp->b_wptr = mp->b_rptr + 2;
		mp->b_rptr[0] = err < 0 ? -err : err;
		mp->b_rptr[1] = err < 0 ? -err : err;
		LOGTX(xp2sid(xp), "<- M_ERROR");
		qreply(q, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/*
 *  SLI PRIMITIVES ISSUED UPSTREAM
 *  -------------------------------------------------------------------------
 */

/** sl_flush_wq: - flush write queue of active stream
  * @ch: channel structure (locked)
  *
  * This function is called from the state machine (but only under the context of the write queue
  * put or service routine).  The function should remove M_DATA and SL_PDU_REQ messages only instead
  * of flushing all M_PROTO, M_PCPROTO, M_DATA and M_DELAY messages.
  */
static inline fastcall __unlikely void
sl_flush_wq(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL))
		flushq(xp->wq, FLUSHDATA);
}

/** sl_flush_rq: - flush read queue of active stream
  * @ch: channel structure (locked)
  *
  * This function is called from the state machine (but only under the context of the write queue
  * put or service routine).  The function should remove M_DATA and SL_PDU_REQ messages only instead
  * of flushing all M_PROTO, M_PCPROTO, M_DATA and M_DELAY messages.
  */
static inline fastcall __unlikely void
sl_flush_rq(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL))
		flushq(xp->rq, FLUSHDATA);
}

/** sl_enable_rq: - flush read queue of active stream
  * @ch: channel structure (locked)
  */
static inline fastcall __unlikely void
sl_enable_rq(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL))
		qenable(xp->rq);
}

/** sl_count_rq: - count read queue of active stream
  * @ch: channel structure (locked)
  */
static inline fastcall __hot_write uint
sl_count_rq(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL))
		return xp->rq->q_count;
	else
		return (0);
}

/** sl_data_ind: issue M_DATA message upstream
  * @ch: channel structure (locked)
  * @mp: the M_DATA message
  */
static inline fastcall __hot_read int
sl_data_ind(struct ch *ch, mblk_t *mp)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		put(xp->rq, mp);
		return (0);
	} else {
		freemsg(mp);
		return (-ENXIO);
	}
}

/** sl_pdu_ind: issue SL_PDU_IND message upstream
  * @ch: channel structure (locked)
  * @dp: data portion of message
  *
  * We don't actually use SL_PDU_INDs, we pass along M_DATA messages.  This function is never
  * called.
  */
static inline fastcall __hot_read int
sl_pdu_ind(struct ch *ch, mblk_t *dp)
{
	struct xp *xp;

	/* FIXME: this is where we do SL monitoring */
	if (likely((xp = ch->xp) != NULL)) {
#if 0
		sl_pdu_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sl_primitive = SL_PDU_IND;
			mp->b_cont = dp;
			LOGTX(xp2sid(xp), "<- SL_PDU_IND");
			put(xp->rq, mp);
			return (0);
		} else {
			return (-ENOBUFS);
		}
#else
		put(xp->rq, dp);
		ch->sl.stats.sl_recv_msus++;
		return (0);
#endif
	} else {
		return (-ENXIO);
	}
}

/** sl_link_congested_ind: - issue SL_LINK_CONGESTED_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue) or NULL
  * @mp: message to reuse or NULL
  *
  * There are two ways that SL_LINK_CONGESTED_IND can be issued: (1) in response adding a message
  * for transmision (SL_PDU_REQ or M_DATA); (2) in response to an autonomous event within the state
  * machine.  In the first case, @q and @mp will be non-null and the xp structure will be locked
  * because we are called from the context of the write queue put or service procedure.  In the
  * later case, @q and @mp are NULL, xp is unlocked (and might not exist), and we are being called
  * from the tasklet.
  */
noinline fastcall __unlikely int
sl_link_congested_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sl_link_cong_ind_t *p;

		if (mp || (mp = mi_allocb(q ? : xp->rq, sizeof(*p), BPRI_MED))) {
			mp->b_band = 0;
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_rptr;
			mp->b_wptr = mp->b_rptr + sizeof(*p);
			p->sl_primitive = SL_LINK_CONGESTED_IND;
			p->sl_cong_status = ch->sl.statem.cong_status;
			p->sl_disc_status = ch->sl.statem.disc_status;
			/* FIXME: clear bit indicating that SL_LINK_CONGESTED_IND is required. */
			LOGTX(xp2sid(xp), "<- SL_LINK_CONGESTED_IND");
			put(q ? RD(q) : xp->rq, mp);
			return (0);
		}
		/* FIXME: set bit indicating that SL_LINK_CONGESTED_IND is required. */
		rare();
		return (-ENOBUFS);
	}
	freemsg(mp);
	return (0);
}

/** sl_link_congestion_ceased_ind: - issue SL_LINK_CONGESTION_CEASED_IND
  * @ch: channel structure (locked)
  * @q: active queue (write queue) or NULL
  * @mp: message to reuse or NULL
  */
noinline fastcall __unlikely int
sl_link_congestion_ceased_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sl_link_cong_ceased_ind_t *p;

		if (mp || (mp = mi_allocb(q ? : xp->rq, sizeof(*p), BPRI_MED))) {
			mp->b_band = 0;
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_rptr;
			mp->b_wptr = mp->b_rptr + sizeof(*p);
			p->sl_primitive = SL_LINK_CONGESTION_CEASED_IND;
			p->sl_timestamp = jiffies;
			p->sl_cong_status = ch->sl.statem.cong_status;
			p->sl_disc_status = ch->sl.statem.disc_status;
			/* FIXME: clear bit indicating that SL_LINK_CONGESTION_CEASED_IND is required. */
			LOGTX(xp2sid(xp), "<- SL_LINK_CONGESTION_CEASED_IND");
			put(q ? RD(q) : xp->rq, mp);
			return (0);
		}
		/* FIXME: set bit indicating that SL_LINK_CONGESTION_CEASED_IND is required. */
		rare();
		return (-ENOBUFS);
	}
	freemsg(mp);
	return (0);
}

/** sl_retrieved_message_ind: - issue SL_RETRIEVED_MESSAGE_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @dp: data portion of message
  *
  * This is called in response to SL_RETRIEVAL_REQUEST_AND_FSNC_REQ received on write queue from
  * within the write put or service procedure.  The xp structure is locked.
  */
noinline fastcall __unlikely int
sl_retrieved_message_ind(struct ch *ch, queue_t *q, mblk_t *dp)
{
	struct xp *xp = XP_PRIV(q);
	sl_retrieved_msg_ind_t *p;
	mblk_t *mp;

	if ((mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
		DB_TYPE(mp) = M_PROTO;
		p = (typeof(p)) mp->b_wptr;
		mp->b_wptr += sizeof(*p);
		p->sl_primitive = SL_RETRIEVED_MESSAGE_IND;
		mp->b_cont = dp;
		LOGTX(xp2sid(xp), "<- SL_RETRIEVED_MESSGAGE_IND");
		putq(xp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/** sl_retrieval_complete_ind: - issue SL_RETRIEVAL_COMPLETE_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: SL_RETRIEVAL_REQUEST_AND_FSNC_REQ primitive
  *
  * This is called in response to SL_RETRIEVAL_REQUEST_AND_FSNC_REQ received on write queue from
  * within the write put or service procedure.  The xp structure is locked.
  */
noinline fastcall __unlikely void
sl_retrieval_complete_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	sl_retrieval_comp_ind_t *p;

	mp->b_band = 0;
	DB_TYPE(mp) = M_PROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->sl_primitive = SL_RETRIEVAL_COMPLETE_IND;
	LOGTX(xp2sid(xp), "<- SL_RETIREVAL_COMPLETE_IND");
	putq(xp->rq, mp);
}

/** sl_rb_cleared_ind: - issue SL_RB_CLEARED_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: message to reuse
  *
  * This is called in response to SL_CLEAR_BUFFERS_REQ received on write queue from within the write
  * put or service procedure.  The xp structure is locked.
  */
noinline fastcall __unlikely void
sl_rb_cleared_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	sl_rb_cleared_ind_t *p;

	mp->b_band = 0;
	DB_TYPE(mp) = M_PROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->sl_primitive = SL_RB_CLEARED_IND;
	LOGTX(xp2sid(xp), "<- SL_RB_CLEARED_IND");
	putq(xp->rq, mp);
}

/** sl_bsnt_ind: - issue SL_BSNT_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: SL_RETRIEVE_BSNT_REQ primitive
  *
  * This is called in response to SL_RETRIEVE_BSNT_REQ received on write queue, from within the
  * write put or service procedure.  The xp structure is locked.
  */
noinline fastcall __unlikely void
sl_bsnt_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	sl_bsnt_ind_t *p;

	mp->b_band = 0;
	DB_TYPE(mp) = M_PROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->sl_primitive = SL_BSNT_IND;
	p->sl_bsnt = ch->sl.statem.rx.T.bsn;
	LOGTX(xp2sid(xp), "<- SL_BSNT_IND");
	putq(xp->rq, mp);
}

/** sl_in_service_ind: - issue an SL_IN_SERVICE_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is only issued from the bottom-half tasklet as a result of receiving the
  * appropriate message sequence for initial alignement.
  */
noinline fastcall void
sl_in_service_ind(struct ch *ch)
{
	if (ch->sl.statem.failure_reason == 0) {
		struct xp *xp;

		if (likely((xp = ch->xp) != NULL)) {
			sl_in_service_ind_t *p;
			mblk_t *mp;

			if ((mp = mi_allocb(xp->rq, sizeof(*p), BPRI_MED))) {
				DB_TYPE(mp) = M_PROTO;
				p = (typeof(p)) mp->b_wptr;
				mp->b_wptr += sizeof(*p);
				p->sl_primitive = SL_IN_SERVICE_IND;
				/* FIXME: clear bit indicating that we need SL_IN_SERVICE_IND. */
				LOGTX(xp2sid(xp), "<- SL_IN_SERVICE_IND");
				put(xp->rq, mp);
			} else {
				/* FIXME: set bit indicating that we need SL_IN_SERVICE_IND. */
				rare();
			}
		}
	}
}

/** sl_out_of_service_ind: - issue SL_OUT_OF_SERVICE_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is issued from two contexts: either the primitive is issued as a result of a
  * timeout; otherwise, it is issued as a result of autonomous events within the signalling link
  * state machine.  For the timeout, the execution context is the timer callback procedure.  For the
  * autonomous event, the execution context is the bottom-half tasklet.  Both are technically
  * running at bottom half and bottom half suppression is sufficient for ch->lock from process
  * context.
  *
  * The only reason that timeout processing would ever fail in the SL state machine is because this
  * function returns non-zero (fails to allocate a buffer).  Therefore, when we have an active
  * stream associated with the signalling link state machine, we use mi_allocb(9) to allocate the
  * message block and schedule the read queue for enabling on the bufcall(9) callback.  This way,
  * the read service procedure will run when a buffer is available.  A wakeup flag is set indicating
  * that a link out of service condition has occurred so that the wakeup routine of the read service
  * procedure can attempt to issue the indication at that time.  This is a better approach than
  * attempting to defer timeouts.  Care should be taken that the read service wakeup procedure is
  * run *before* the queue is processed for messages, otherwise, a message order reversal could
  * occur.
  */
noinline fastcall void
sl_out_of_service_ind(struct ch *ch)
{
	if (ch->sl.statem.failure_reason) {
		struct xp *xp;

		if (likely((xp = ch->xp) != NULL)) {
			sl_out_of_service_ind_t *p;
			mblk_t *mp;

			if ((mp = mi_allocb(xp->rq, sizeof(*p), BPRI_MED))) {
				DB_TYPE(mp) = M_PROTO;
				p = (typeof(p)) mp->b_wptr;
				mp->b_wptr += sizeof(*p);
				p->sl_primitive = SL_OUT_OF_SERVICE_IND;
				p->sl_timestamp = jiffies;
				p->sl_reason = ch->sl.statem.failure_reason;
				/* FIXME: clear bit indicating that we need SL_OUT_OF_SERVICE_IND. */
				LOGTX(xp2sid(xp), "<- SL_OUT_OF_SERVICE_IND");
				put(xp->rq, mp);
			} else {
				/* FIXME: set bit indicating that we need SL_OUT_OF_SERVICE_IND. */
				rare();
			}
		}
	}
}

/** sl_remote_processor_outage_ind: - issue SL_REMOTE_PROCESSOR_OUTAGE_IND
  * @ch: channel structure (locked)
  * @q: active queue (write queue) or NULL
  *
  * This primitive is issued from two contexts: either the primitive is issued as a result of a
  * SL_RESUME_REQ being received on the write queue, in which case @q will be non-NULL; otherwise,
  * it is issued as a result of autonomous events (receiving SIPO) within the signalling link state
  * machine.  For the SL_RESUME_REQ, the execution context is the write queue put or service
  * procedure.  For the autonomous event, the execution context is the bottom-half tasklet.
  */
noinline fastcall __unlikely int
sl_remote_processor_outage_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	if (ch->sl.statem.remote_processor_outage
	    || ch->sl.statem.poc_state == SL_STATE_REMOTE_PROCESSOR_OUTAGE
	    || ch->sl.statem.poc_state == SL_STATE_BOTH_PROCESSORS_OUT) {
		struct xp *xp;

		if (likely((xp = ch->xp) != NULL)) {
			sl_rem_proc_out_ind_t *p;

			if (mp || (mp = mi_allocb(q ? : xp->rq, sizeof(*p), BPRI_MED))) {
				mp->b_band = 0;
				DB_TYPE(mp) = M_PROTO;
				p = (typeof(p)) mp->b_rptr;
				mp->b_wptr = mp->b_rptr + sizeof(*p);
				p->sl_primitive = SL_REMOTE_PROCESSOR_OUTAGE_IND;
				p->sl_timestamp = jiffies;
				/* FIXME: clear bit indicating that we need SL_OUT_OF_SERVICE */
				LOGTX(xp2sid(xp), "<- SL_REMOTE_PROCESSOR_OUTAGE_IND");
				put(q ? RD(q) : xp->rq, mp);
				return (0);
			}
			/* FIXME: set bit indicating that we need SL_OUT_OF_SERVICE */
			rare();
			return (-ENOBUFS);
		}
	}
	freemsg(mp);
	return (0);
}

/** sl_remote_processor_recovered_ind: - issue SL_REMOTE_PROCESSOR_RECOVERED_IND
  * @ch: channel structure (locked)
  *
  * This primitive is only issued from the bottom-half tasklet.
  */
noinline fastcall __unlikely void
sl_remote_processor_recovered_ind(struct ch *ch)
{
	if (ch->sl.statem.remote_processor_outage == 0
	    && ch->sl.statem.poc_state != SL_STATE_REMOTE_PROCESSOR_OUTAGE
	    && ch->sl.statem.poc_state != SL_STATE_BOTH_PROCESSORS_OUT) {
		struct xp *xp;

		if (likely((xp = ch->xp) != NULL)) {
			sl_rem_proc_recovered_ind_t *p;
			mblk_t *mp;

			if ((mp = mi_allocb(xp->rq, sizeof(*p), BPRI_MED))) {
				DB_TYPE(mp) = M_PROTO;
				p = (typeof(p)) mp->b_wptr;
				mp->b_wptr += sizeof(*p);
				p->sl_primitive = SL_REMOTE_PROCESSOR_RECOVERED_IND;
				p->sl_timestamp = jiffies;
				/* FIXME: clear bit indicating that we need
				   SL_REMOTE_PROCESSOR_RECOVERED_IND. */
				LOGTX(xp2sid(xp), "<- SL_REMOTE_PROCESSOR_RECOVERED_IND");
				put(xp->rq, mp);
			} else {
				/* FIXME: set bit indicating that we need SL_REMOTE_PROCESSOR_RECOVERED_IND. */
				rare();
			}
		}
	}
}

/** sl_rtb_cleared_ind: - issue SL_RTB_CLEARED_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue) or NULL
  * @mp: message to reuse or NULL
  *
  * This is called in response to 
  */
noinline fastcall __unlikely int
sl_rtb_cleared_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp;

	/* FIXME: verify that condition still exists */
	if (likely((xp = ch->xp) != NULL)) {
		sl_rtb_cleared_ind_t *p;

		if (mp || (mp = mi_allocb(q ? : xp->rq, sizeof(*p), BPRI_MED))) {
			mp->b_band = 0;
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_rptr;
			mp->b_wptr = mp->b_rptr + sizeof(*p);
			p->sl_primitive = SL_RTB_CLEARED_IND;
			/* FIXME: clear bit indicating that we need SL_RTB_CLEARED_IND. */
			LOGTX(xp2sid(xp), "<- SL_RTB_CLEARED_IND");
			put(q ? RD(q) : xp->rq, mp);
			return (0);
		}
		/* FIXME: set bit indicating that we need SL_RTB_CLEARED_IND. */
		rare();
		return (-ENOBUFS);
	}
	freemsg(mp);
	return (0);
}

#if 1
/** sl_retrieval_not_possible_ind: - issue SL_RETREIVAL_NOT_POSSIBLE_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue) or NULL
  * @mp: message to reuse or NULL
  *
  * This message is never issued because in the integrated driver retrieval is always possible.
  */
static inline fastcall __unlikely int
sl_retrieval_not_possible_ind(struct ch *ch, queue_t *q, mblk_t *mp)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sl_retrieval_not_poss_ind_t *p;

		if (mp || (mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
			mp->b_band = 0;
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_rptr;
			mp->b_wptr = mp->b_rptr + sizeof(*p);
			p->sl_primitive = SL_RETRIEVAL_NOT_POSSIBLE_IND;
			LOGTX(xp2sid(xp), "<- SL_RETRIEVAL_NOT_POSSIBLE_IND");
			putq(RD(q), mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	freemsg(mp);
	return (0);
}

/** sl_bsnt_not_retrievable_ind: - issue SL_BSNT_NOT_RETRIEVABLE_IND primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue) or NULL
  * @mp: message to reuse or NULL
  *
  * This message is never issued because in the integrated driver BSNT retrieval is always possible.
  */
static inline fastcall __unlikely int
sl_bsnt_not_retrievable_ind(struct ch *ch, queue_t *q, mblk_t *mp, sl_ulong bsnt)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sl_bsnt_not_retr_ind_t *p;

		if (mp || (mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
			mp->b_band = 0;
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_rptr;
			mp->b_wptr = mp->b_rptr + sizeof(*p);
			p->sl_primitive = SL_BSNT_NOT_RETRIEVABLE_IND;
			p->sl_bsnt = bsnt;
			LOGTX(xp2sid(xp), "<- SL_BSNT_NOT_RETRIEVABLE_IND");
			putq(RD(q), mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	freemsg(mp);
	return (0);
}
#endif

#if 0
/*
 *  SL_OPTMGMT_ACK
 *  -----------------------------------
 */
static inline fastcall __unlikely int
sl_optmgmt_ack(struct xp *xp, queue_t *q, mblk_t *rp, caddr_t opt_ptr, size_t opt_len, sl_ulong flags)
{
	mblk_t *mp;
	sl_optmgmt_ack_t *p;

	if ((mp = mi_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		DB_TYPE(mp) = M_PROTO;
		p = (typeof(p)) mp->b_wptr;
		mp->b_wptr += sizeof(*p);
		p->sl_primitive = SL_OPTMGMT_ACK;
		p->opt_length = opt_len;
		p->opt_offset = opt_len ? sizeof(*p) : 0;
		p->mgmt_flags = flags;
		bcopy(opt_ptr, mp->b_wptr, opt_len);
		mp->b_wptr += opt_len;
		freemsg(rp);
		LOGTX(xp2sid(xp), "<- SL_OPTMGMT_ACK");
		putnext(RD(q), mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/** sl_notify_ind: - issue SL_NOTIFY_IND primitive
  * @xp: private structure (locked)
  * @q: active queue (or NULL)
  * @oid: object identifier
  * @level: severity level
  *
  * Currently this primitive is never issued.
  *
  * TODO: We should use this primitive (or LMI_EVENT_IND) to deliver notification of first-and-delta
  * events and other SS7 management events.
  */
static inline fastcall __unlikely int
sl_notify_ind(struct xp *xp, queue_t *q, sl_ulong oid, sl_ulong level)
{
	static const int nb = 2;

	if (likely(bcanput(xp->rq, nb))) {
		lmi_event_ind_t *p;
		mblk_t *mp;

		if ((mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			mp->b_band = nb;	/* band it up */
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->lmi_primitive = SL_NOTIFY_IND;
			p->lmi_objectid = oid;
			p->lmi_timestamp = jiffies;
			p->lmi_severity = level;
			LOGTX(xp2sid(xp), "<- SL_NOTIFY_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}
#endif

/*
 *  SDTI PRIMITIVES ISSUED UPSTREAM
 *  -------------------------------------------------------------------------
 */

/** sdt_rc_signal_unit_ind: - issue SDT_RC_SIGNAL_UNIT_IND primitive
  * @ch: channel structure (locked)
  * @dp: the M_DATA portion of the message
  * @count: the number of repetitions of a FISU or LSSU
  *
  * We prefer to send M_DATA blocks.  When the count is 1, we simply send M_DATA.  When the count is
  * greater than one, we send an SDT_RC_SIGNAL_UNIT_IND which also includes the count.  This is so
  * that upper layer modules can collect SU statistics.
  *
  * Can't use buffer service.
  *
  * Note: this is the wrong place to do buffer duping: this is executed within a tasklet and needs
  * to return fast to keep soft-HDLC caches hot.  The proper place to perform the duping is in the
  * read side service procedure.
  */
static inline fastcall __hot_in int
sdt_rc_signal_unit_ind(struct ch *ch, mblk_t *dp, sl_ulong count)
{
	struct xp *xp;

	/* FIXME: here is where we do monitoring of SDT messages */
	if (likely((xp = ch->xp) != NULL)) {
		if (likely(count)) {
			if (likely(canput(xp->rq))) {
				sdt_rc_signal_unit_ind_t *p;
				mblk_t *mp;

				if (likely((mp = allocb(sizeof(*p), BPRI_MED)) != NULL)) {
					DB_TYPE(mp) = M_PROTO;
					p = (typeof(p)) mp->b_wptr;
					mp->b_wptr += sizeof(*p);
					p->sdt_primitive = SDT_RC_SIGNAL_UNIT_IND;
					p->sdt_count = count;
					mp->b_cont = dp;
					LOGDA(xp2sid(xp), "<- SDT_RC_SIGNAL_UNIT_IND");
					put(xp->rq, mp);
					/* FIXME: NEED to dup to the XRAY chain */
					return (0);
				}
				rare();
				return (-ENOBUFS);
			}
			rare();
			return (-EBUSY);
		}
		swerr();
		return (-EFAULT);
	}
	return (-ENXIO);
}

/* Note: none of the following SDT primitives are actually issued.  They are signals that are
 * delivered to the signalling link level internally when required.  Implementing them requires
 * splitting the signalling terminal state machine from the signalling link state machine. */

#if 1
/** sdt_rc_congestion_accept_ind: - issue SDT_RC_CONGESTION_ACCEPT_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
noinline fastcall __unlikely int
sdt_rc_congestion_accept_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdt_rc_congestion_accept_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdt_primitive = SDT_RC_CONGESTION_ACCEPT_IND;
			LOGTX(xp2sid(xp), "<- SDT_RC_CONGESTION_ACCEPT_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}

/** sdt_rc_congestion_discard_ind: - issue SDT_RC_CONGESTION_DISCARD_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
noinline fastcall __unlikely int
sdt_rc_congestion_discard_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdt_rc_congestion_discard_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdt_primitive = SDT_RC_CONGESTION_DISCARD_IND;
			LOGTX(xp2sid(xp), "<- SDT_RC_CONGESTION_DISCARD_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}

/** sdt_rc_no_congestion_ind: - issue SDT_RC_NO_CONGESTION_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
noinline fastcall __unlikely int
sdt_rc_no_congestion_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdt_rc_no_congestion_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdt_primitive = SDT_RC_NO_CONGESTION_IND;
			LOGTX(xp2sid(xp), "<- SDT_RC_NO_CONGESTION_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}

/** sdt_iac_correct_su_ind: - issue SDT_IAC_CORRECT_SU_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
static inline fastcall __hot_read int
sdt_iac_correct_su_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		if (canputnext(xp->rq)) {
			sdt_iac_correct_su_ind_t *p;
			mblk_t *mp;

			if ((mp = allocb(sizeof(*p), BPRI_MED))) {
				DB_TYPE(mp) = M_PROTO;
				p = (typeof(p)) mp->b_wptr;
				mp->b_wptr += sizeof(*p);
				p->sdt_primitive = SDT_IAC_CORRECT_SU_IND;
				LOGTX(xp2sid(xp), "<- SDT_IAC_CORRECT_SU_IND");
				put(xp->rq, mp);
				return (0);
			}
			rare();
			return (-ENOBUFS);
		}
		rare();
		return (-EBUSY);
	}
	return (-ENXIO);
}

/** sdt_iac_abort_proving_ind: - issue SDT_IAC_ABORT_PROVING_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
noinline fastcall int
sdt_iac_abort_proving_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdt_iac_abort_proving_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdt_primitive = SDT_IAC_ABORT_PROVING_IND;
			LOGTX(xp2sid(xp), "<- SDT_IAC_ABORT_PROVING_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}

/** sdt_lsc_link_failure_ind: - issue SDT_LSC_LINK_FAILURE_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
noinline fastcall __unlikely int
sdt_lsc_link_failure_ind(struct ch *ch, queue_t *q)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdt_lsc_link_failure_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdt_primitive = SDT_LSC_LINK_FAILURE_IND;
			LOGTX(xp2sid(xp), "<- SDT_LSC_LINK_FAILURE_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}

/** sdt_txc_transmission_request_ind: - issue SDT_TXC_TRANSMISSION_REQUEST_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDT-only mode.
  */
static inline fastcall __hot_out int
sdt_txc_transmission_request_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdt_txc_transmission_request_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdt_primitive = SDT_TXC_TRANSMISSION_REQUEST_IND;
			LOGDA(xp2sid(xp), "<- SDT_TXC_TRANSMISSION_REQUEST_IND");
			putnext(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}
#endif

/*
 *  SDLI PRIMITIVES ISSUED UPSTREAM
 *  -------------------------------------------------------------------------
 */

/** sdl_received_bits_ind: - issue SDL_RECEIVED_BITS_IND primitive
  * @ch: channel structure (locked)
  * @dp: M_DATA portion of message
  */
static inline fastcall __hot_in int
sdl_received_bits_ind(struct ch *ch, mblk_t *dp)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		if (canput(xp->rq)) {
			LOGDA(xp2sid(xp), "<- SDL_RECEIVED_BITS_IND");
			put(xp->rq, dp);
			return (0);
		}
		rare();
		dp->b_wptr = dp->b_rptr;	/* discard contents */
		return (-EBUSY);
	}
	dp->b_wptr = dp->b_rptr;	/* discard contents */
	return (-ENXIO);
}

/* Note: none of the following SDL primitives are actually issued.  We do not provide disconnect
 * indications because SS7 does not examine "leads". */

#if 1
/** sdl_disconnect_ind: - issue SDL_DISCONNECT_IND primitive
  * @ch: channel structure (locked)
  *
  * This primitive is never issued.  TODO: generate this primitive when operating in SDL-only mode.
  */
static inline fastcall __unlikely int
sdl_disconnect_ind(struct ch *ch)
{
	struct xp *xp;

	if (likely((xp = ch->xp) != NULL)) {
		sdl_disconnect_ind_t *p;
		mblk_t *mp;

		if ((mp = allocb(sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->sdl_primitive = SDL_DISCONNECT_IND;
			LOGTX(xp2sid(xp), "<- SDL_DISCONNECT_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	return (-ENXIO);
}
#endif

/*
 *  LMI PRIMITIVES ISSUED UPSTREAM
 *  -------------------------------------------------------------------------
 */

/** lmi_ok_ack: - issue LMI_OK_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the correct primitive
  * @state: the new state to set
  * @prim: the primitive type of the correct primitive
  */
noinline fastcall __unlikely void
lmi_ok_ack(struct xp *xp, queue_t *q, mblk_t *mp, sl_ulong state, sl_long prim)
{
	lmi_ok_ack_t *p;

	mp->b_band = 0;
	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->lmi_primitive = LMI_OK_ACK;
	p->lmi_correct_primitive = prim;
	p->lmi_state = state;
	xp_set_state(xp, state);
	LOGTX(xp2sid(xp), "<- LMI_OK_ACK");
	qreply(q, mp);
}

/** lmi_error_ack: - issue LMI_ERROR_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: primitive in error
  * @state: the new state to set
  * @prim: the primitive type of the primitive in error
  * @errno: the UNIX error number
  * @reason: the DLPI error number
  */
noinline fastcall __unlikely void
lmi_error_ack(struct xp *xp, queue_t *q, mblk_t *mp, sl_ulong state, sl_long prim, sl_ulong errno,
	      sl_ulong reason)
{
	lmi_error_ack_t *p;

	mp->b_band = 0;
	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->lmi_primitive = LMI_ERROR_ACK;
	p->lmi_errno = errno;
	p->lmi_reason = reason;
	p->lmi_error_primitive = prim;
	p->lmi_state = state;
	xp_set_state(xp, state);
	LOGTX(xp2sid(xp), "<- LMI_ERROR_ACK");
	qreply(q, mp);
}

/** lmi_info_ack: - issue LMI_INFO_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @rp: message to reuse
  * @ppa_ptr: pointer to ppa (or NULL)
  * @ppa_len: length of ppa (or zero)
  */
static inline fastcall __unlikely int
lmi_info_ack(struct xp *xp, queue_t *q, mblk_t *rp, caddr_t ppa_ptr, size_t ppa_len)
{
	struct ch *ch;
	lmi_info_ack_t *p;
	mblk_t *mp = NULL;

	if (MBLKSIZE(rp) >= sizeof(*p) + ppa_len) {
		mp = rp;
		rp = NULL;
		mp->b_band = 0;
		mp->b_rptr = mp->b_wptr = DB_BASE(mp);
	}
	if (mp || (mp = mi_allocb(q, sizeof(*p) + ppa_len, BPRI_MED))) {
		DB_TYPE(mp) = M_PCPROTO;
		p = (typeof(p)) mp->b_wptr;
		mp->b_wptr += sizeof(*p);
		p->lmi_primitive = LMI_INFO_ACK;
		p->lmi_version = LMI_CURRENT_VERSION;
		p->lmi_state = xp_get_state(xp);
		if ((ch = xp->ch)) {
			if (ch->option.popt & SS7_POPT_XSN) {
				p->lmi_max_sdu = ch->sdt.config.m + 1 + 6;
				p->lmi_min_sdu = 6;
			} else {
				p->lmi_max_sdu = ch->sdt.config.m + 1 + 3;
				p->lmi_min_sdu = 3;
			}
		} else {
			if (xp->chan == 0) {
				p->lmi_max_sdu = 272 + 1 + 6;
				p->lmi_min_sdu = 6;
			} else {
				p->lmi_max_sdu = 272 + 1 + 3;
				p->lmi_min_sdu = 3;
			}
		}
		p->lmi_header_len = 0;
		p->lmi_ppa_style = LMI_STYLE2;
		p->lmi_ppa_length = ppa_len;
		p->lmi_ppa_offset = sizeof(*p);
		p->lmi_prov_flags = xp->i_flags;
		p->lmi_prov_state = xp->i_state;
		fixme(("%s: maintain provider flags", __FUNCTION__));
		bcopy(ppa_ptr, mp->b_wptr, ppa_len);
		mp->b_wptr += ppa_len;
		freemsg(rp);
		LOGTX(xp2sid(xp), "<- LMI_INFO_ACK");
		putnext(RD(q), mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/** lmi_enable_con: - issue LMI_ENABLE_CON primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_ENABLE_REQ primitive
  */
static inline fastcall __unlikely void
lmi_enable_con(struct xp *xp, queue_t *q, mblk_t *mp)
{
	lmi_enable_con_t *p;

	mp->b_band = 0;
	DB_TYPE(mp) = M_PROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->lmi_primitive = LMI_ENABLE_CON;
	p->lmi_state = LMI_ENABLED;
	xp_set_state(xp, LMI_ENABLED);
	LOGTX(xp2sid(xp), "<- LMI_ENABLE_CON");
	qreply(q, mp);
}

/** lmi_disable_con: - issue LMI_DISABLE_CON primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_DISABLE_REQ primitive
  */
static inline fastcall __unlikely void
lmi_disable_con(struct xp *xp, queue_t *q, mblk_t *mp)
{
	lmi_disable_con_t *p;

	putctl2(RD(q), M_FLUSH, FLUSHRW, 0);

	mp->b_band = 0;
	DB_TYPE(mp) = M_PROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->lmi_primitive = LMI_DISABLE_CON;
	p->lmi_state = LMI_DISABLED;
	xp_set_state(xp, LMI_DISABLED);
	LOGTX(xp2sid(xp), "<- LMI_DISABLE_CON");
	qreply(q, mp);
}

#if 1
/** lmi_optmgmt_ack: - issue LMI_OPTMGMT_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @rp: the LMI_OPTMGMT_REQ primitive
  * @flags: management flags
  * @opt_ptr: options pointer (or NULL)
  * @opt_len: options length (or zero)
  *
  * Currently this primitive is never issued.
  */
static inline fastcall __unlikely int
lmi_optmgmt_ack(struct xp *xp, queue_t *q, mblk_t *rp, sl_ulong flags, caddr_t opt_ptr, size_t opt_len)
{
	mblk_t *mp = NULL;
	lmi_optmgmt_ack_t *p;

	if (MBLKSIZE(rp) >= sizeof(*p) + opt_len) {
		mp = rp;
		rp = NULL;
		mp->b_wptr = mp->b_rptr = DB_BASE(mp);
		mp->b_band = 0;
	}
	if (mp || (mp = mi_allocb(q, sizeof(*p) + opt_len, BPRI_MED))) {
		DB_TYPE(mp) = M_PCPROTO;
		p = (typeof(p)) mp->b_wptr;
		mp->b_wptr += sizeof(*p);
		p->lmi_primitive = LMI_OPTMGMT_ACK;
		p->lmi_opt_length = opt_len;
		p->lmi_opt_offset = sizeof(*p);
		p->lmi_mgmt_flags = flags;
		if (opt_len) {
			bcopy(opt_ptr, mp->b_wptr, opt_len);
			mp->b_wptr += opt_len;
		}
		freemsg(rp);
		LOGTX(xp2sid(xp), "<- LMI_OPTMGMT_ACK");
		qreply(q, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/** lmi_error_ind: - issue LMI_ERROR_IND primitive
  * @xp: private structure (locked)
  * @q: active queue
  * @rp: message to reuse (or NULL)
  * @errno: UNIX error number
  * @reason: LMI error number
  *
  * Currently this primitive is never issued.
  */
static inline fastcall __unlikely int
lmi_error_ind(struct xp *xp, queue_t *q, mblk_t *rp, sl_ulong errno, sl_ulong reason)
{
	mblk_t *mp = NULL;
	lmi_error_ind_t *p;

	if (MBLKSIZE(rp) >= sizeof(*p)) {
		mp = rp;
		rp = NULL;
		mp->b_wptr = mp->b_rptr = DB_BASE(mp);
		mp->b_band = 0;
	}
	if (mp || (mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
		DB_TYPE(mp) = M_PROTO;
		p = (typeof(p)) mp->b_wptr;
		mp->b_wptr += sizeof(*p);
		p->lmi_primitive = LMI_ERROR_IND;
		p->lmi_errno = errno;
		p->lmi_reason = reason;
		p->lmi_state = xp_get_state(xp);
		LOGTX(xp2sid(xp), "<- LMI_ERROR_IND");
		put(xp->rq, mp);
		return (0);
	}
	rare();
	return (-ENOBUFS);
}

/** lmi_stats_ind: - issue LMI_STATS_IND primitive
  * @xp: private structure (locked)
  * @q: active queue
  * @interval: statistics interval number
  * @sta_ptr: pointer to specific statistics structure
  * @sta_len: length of specific statistics structure
  *
  * Currently this primitive is never issued.
  *
  * TODO: We should use this primitive to deliver 5-minute statistics at the end of every 5-minute
  * interval.
  */
static inline fastcall __unlikely int
lmi_stats_ind(struct xp *xp, queue_t *q, sl_ulong interval, caddr_t sta_ptr, size_t sta_len)
{
	if (likely(canput(xp->rq))) {
		lmi_stats_ind_t *p;
		mblk_t *mp;

		if ((mp = mi_allocb(q, sizeof(*p) + sta_len, BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->lmi_primitive = LMI_STATS_IND;
			p->lmi_interval = interval;
			p->lmi_timestamp = jiffies;
			if (sta_len) {
				bcopy(sta_ptr, mp->b_wptr, sta_len);
				mp->b_wptr += sta_len;
			}
			LOGTX(xp2sid(xp), "<- LMI_STATS_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}

/** lmi_event_ind: - issue LMI_EVENT_IND primitive
  * @xp: private structure (locked)
  * @q: active queue (or NULL)
  * @oid: object identifier
  * @level: severity level
  *
  * Currently this primitive is never issued.
  *
  * TODO: We should use this primitive (or SL_NOTIFY_IND) to deliver notification of first-and-delta
  * events and other SS7 management events.
  */
static inline fastcall __unlikely int
lmi_event_ind(struct xp *xp, queue_t *q, sl_ulong oid, sl_ulong level)
{
	static const int nb = 2;

	if (likely(bcanput(xp->rq, nb))) {
		lmi_event_ind_t *p;
		mblk_t *mp;

		if ((mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
			DB_TYPE(mp) = M_PROTO;
			mp->b_band = nb;	/* band it up */
			p = (typeof(p)) mp->b_wptr;
			mp->b_wptr += sizeof(*p);
			p->lmi_primitive = LMI_EVENT_IND;
			p->lmi_objectid = oid;
			p->lmi_timestamp = jiffies;
			p->lmi_severity = level;
			LOGTX(xp2sid(xp), "<- LMI_EVENT_IND");
			put(xp->rq, mp);
			return (0);
		}
		rare();
		return (-ENOBUFS);
	}
	rare();
	return (-EBUSY);
}
#endif

/*
 *  DLPI PRIMITIVES ISSUED UPSTREAM
 *  -------------------------------------------------------------------------
 */

/** dl_error_ack: - issue DL_ERROR_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: primitive in error
  * @prim: primitive type of primtiive in error
  * @errno: UNIX error number
  * @errno: DLPI error number
  */
static inline fastcall __unlikely void
dl_error_ack(struct xp *xp, queue_t *q, mblk_t *mp, int prim, int errno, uint reason)
{
	dl_error_ack_t *p;

	mp->b_band = 0;
	mp->b_flag = 0;
	mp->b_rptr = mp->b_wptr = DB_BASE(mp);
	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->dl_primitive = DL_ERROR_ACK;
	p->dl_error_primitive = prim;
	p->dl_errno = reason;
	p->dl_unix_errno = errno < 0 ? -errno : errno;
	LOGTX(xp2sid(xp), "<- DL_ERROR_ACK");
	qreply(q, mp);
	return;
}

/** dl_ok_ack: - issue DL_OK_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the correct primitive
  * @state: the new state to set
  * @prim: the primitive type of the correct primitive
  */
static inline fastcall __hot_in void
dl_ok_ack(struct xp *xp, queue_t *q, mblk_t *mp, dl_ulong state, dl_long prim)
{
	dl_ok_ack_t *p;

	mp->b_band = 0;
	mp->b_flag = 0;
	mp->b_rptr = mp->b_wptr = DB_BASE(mp);
	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->dl_primitive = DL_OK_ACK;
	p->dl_correct_primitive = prim;
	dl_set_state(xp, state);
	LOGTX(xp2sid(xp), "<- DL_OK_ACK");
	qreply(q, mp);
	return;
}

/** dl_bind_ack: - issue DL_BIND_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_BIND_REQ primitive
  * @dl_sap: the SAP that was set
  * @dl_max_conind: the maximum number of connection indications
  */
static inline fastcall __unlikely void
dl_bind_ack(struct xp *xp, queue_t *q, mblk_t *mp, dl_ulong dl_sap, dl_ulong dl_max_conind)
{
	dl_bind_ack_t *p;

	mp->b_band = 0;
	mp->b_flag = 0;
	mp->b_rptr = mp->b_wptr = DB_BASE(mp);
	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->dl_primitive = DL_BIND_ACK;
	p->dl_sap = dl_sap;
	p->dl_addr_length = 0;
	p->dl_addr_offset = 0;
	p->dl_max_conind = dl_max_conind;
	p->dl_xidtest_flg = 0;
	LOGTX(xp2sid(xp), "<- DL_BIND_ACK");
	qreply(q, mp);
	return;
}

/** dl_token_ack: - issue DL_TOKEN_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_TOKEN_REQ primitive
  */
static inline fastcall __unlikely void
dl_token_ack(struct xp *xp, queue_t *q, mblk_t *mp)
{
	dl_token_ack_t *p;

	mp->b_band = 0;
	mp->b_flag = 0;
	mp->b_rptr = mp->b_wptr = DB_BASE(mp);
	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->dl_primitive = DL_TOKEN_ACK;
	p->dl_token = (dl_ulong) (ulong) xp;
	LOGTX(xp2sid(xp), "<- DL_TOKEN_ACK");
	qreply(q, mp);
	return;
}

/** dl_info_ack: - issue DL_INFO_ACK primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @rp: the DL_INFO_REQ primitive
  *
  * The DL_INFO_REQ primitive message block will be reused for the DL_INFO_ACK if it is of
  * sufficient size.  It is, however, likely on 32-bit architectures that the FASTBUF size (used by
  * the DL_INFO_REQ) is too small to contain the DL_INFO_ACK.
  */
static inline fastcall __unlikely int
dl_info_ack(struct xp *xp, queue_t *q, mblk_t *rp)
{
	struct ch *ch;
	dl_info_ack_t *p;
	mblk_t *mp = NULL;

	if (MBLKSIZE(rp) >= sizeof(*p)) {
		mp = rp;
		rp = NULL;
		mp->b_band = 0;
		mp->b_flag = 0;
		mp->b_rptr = mp->b_wptr = DB_BASE(mp);
	}
	if (!mp && !(mp = mi_allocb(q, sizeof(*p), BPRI_MED))) {
		rare();
		return (-ENOBUFS);
	}

	DB_TYPE(mp) = M_PCPROTO;
	p = (typeof(p)) mp->b_rptr;
	mp->b_wptr = mp->b_rptr + sizeof(*p);
	p->dl_primitive = DL_INFO_ACK;
	if ((ch = xp->ch)) {
		if (ch->option.popt & SS7_POPT_XSN) {
			p->dl_max_sdu = ch->sdt.config.m + 1 + 6;
			p->dl_min_sdu = 6;
		} else {
			p->dl_max_sdu = ch->sdt.config.m + 1 + 3;
			p->dl_min_sdu = 3;
		}
	} else {
		if (xp->chan == 0) {
			p->dl_max_sdu = 272 + 1 + 6;
			p->dl_min_sdu = 6;
		} else {
			p->dl_max_sdu = 272 + 1 + 6;
			p->dl_min_sdu = 3;
		}
	}
	p->dl_addr_length = 0;
	p->dl_mac_type = DL_OTHER;
	p->dl_reserved = 0;
	p->dl_current_state = dl_get_state(xp);
	if (xp->monitor) {
		p->dl_sap_length = 4;
#ifdef DL_HP_RAWDLS
		p->dl_service_mode = DL_HP_RAWDLS;
#else
		p->dl_service_mode = DL_CLDLS;
#endif
	} else {
		p->dl_sap_length = 0;
		p->dl_service_mode = DL_CODLS;
	}
	p->dl_qos_length = 0;
	p->dl_qos_offset = 0;
	p->dl_qos_range_length = 0;
	p->dl_qos_range_offset = 0;
	p->dl_provider_style = DL_STYLE2;
	p->dl_addr_offset = 0;
	p->dl_version = DL_CURRENT_VERSION;
	p->dl_brdcst_addr_length = 0;
	p->dl_brdcst_addr_offset = 0;
	p->dl_growth = 0;
	LOGTX(xp2sid(xp), "<- DL_INFO_ACK");
	freemsg(rp);
	qreply(q, mp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  PROTOCOL STATE MACHINE FUNCTIONS
 *
 *  =========================================================================
 */
/*
 *  ------------------------------------------------------------------------
 *
 *  Default Configuration
 *
 *  ------------------------------------------------------------------------
 */
STATIC lmi_option_t lmi_default_e1_chan = {
	.pvar = SS7_PVAR_ITUT_00,
	.popt = 0,
};

STATIC lmi_option_t lmi_default_t1_chan = {
	.pvar = SS7_PVAR_ANSI_00,
	.popt = SS7_POPT_MPLEV,
};

STATIC lmi_option_t lmi_default_j1_chan = {
	.pvar = SS7_PVAR_JTTC_94,
	.popt = SS7_POPT_MPLEV,
};

STATIC lmi_option_t lmi_default_e1_span = {
	.pvar = SS7_PVAR_ITUT_00,
	.popt = SS7_POPT_HSL | SS7_POPT_XSN,
};

STATIC lmi_option_t lmi_default_t1_span = {
	.pvar = SS7_PVAR_ANSI_00,
	.popt = SS7_POPT_MPLEV | SS7_POPT_HSL | SS7_POPT_XSN,
};

STATIC lmi_option_t lmi_default_j1_span = {
	.pvar = SS7_PVAR_JTTC_94,
	.popt = SS7_POPT_MPLEV | SS7_POPT_HSL | SS7_POPT_XSN,
};

#if 1
#define UPS HZ
#else
#define UPS 1000
#endif

STATIC sl_config_t sl_default_e1_chan = {
	.t1 = 45 * UPS,
	.t2 = 5 * UPS,
	.t2l = 20 * UPS,
	.t2h = 100 * UPS,
	.t3 = 1 * UPS,
	.t4n = 8 * UPS,
	.t4e = UPS / 2,
	.t5 = UPS / 10,
	.t6 = 4 * UPS,
	.t7 = 1 * UPS,
	.rb_abate = 3,
	.rb_accept = 6,
	.rb_discard = 9,
	.tb_abate_1 = 128 * 272,
	.tb_onset_1 = 256 * 272,
	.tb_discd_1 = 384 * 272,
	.tb_abate_2 = 512 * 272,
	.tb_onset_2 = 640 * 272,
	.tb_discd_2 = 768 * 272,
	.tb_abate_3 = 896 * 272,
	.tb_onset_3 = 1024 * 272,
	.tb_discd_3 = 1152 * 272,
	.N1 = 127,
	.N2 = 8192,
	.M = 5,
};

STATIC sl_config_t sl_default_e1_span = {
	.t1 = 45 * UPS,
	.t2 = 5 * UPS,
	.t2l = 20 * UPS,
	.t2h = 100 * UPS,
	.t3 = 1 * UPS,
	.t4n = 8 * UPS,
	.t4e = UPS / 2,
	.t5 = UPS / 10,
	.t6 = 4 * UPS,
	.t7 = 1 * UPS,
	.rb_abate = 3,
	.rb_accept = 6,
	.rb_discard = 9,
	.tb_abate_1 = 128 * 272,
	.tb_onset_1 = 256 * 272,
	.tb_discd_1 = 384 * 272,
	.tb_abate_2 = 512 * 272,
	.tb_onset_2 = 640 * 272,
	.tb_discd_2 = 768 * 272,
	.tb_abate_3 = 896 * 272,
	.tb_onset_3 = 1024 * 272,
	.tb_discd_3 = 1152 * 272,
	.N1 = 127,
	.N2 = 8192,
	.M = 5,
};

STATIC sl_config_t sl_default_t1_chan = {
	.t1 = 45 * UPS,
	.t2 = 5 * UPS,
	.t2l = 20 * UPS,
	.t2h = 100 * UPS,
	.t3 = 1 * UPS,
	.t4n = 8 * UPS,
	.t4e = UPS / 2,
	.t5 = UPS / 10,
	.t6 = 4 * UPS,
	.t7 = 1 * UPS,
	.rb_abate = 3,
	.rb_accept = 6,
	.rb_discard = 9,
	.tb_abate_1 = 128 * 272,
	.tb_onset_1 = 256 * 272,
	.tb_discd_1 = 384 * 272,
	.tb_abate_2 = 512 * 272,
	.tb_onset_2 = 640 * 272,
	.tb_discd_2 = 768 * 272,
	.tb_abate_3 = 896 * 272,
	.tb_onset_3 = 1024 * 272,
	.tb_discd_3 = 1152 * 272,
	.N1 = 127,
	.N2 = 8192,
	.M = 5,
};

STATIC sl_config_t sl_default_t1_span = {
	.t1 = 45 * UPS,
	.t2 = 5 * UPS,
	.t2l = 20 * UPS,
	.t2h = 100 * UPS,
	.t3 = 1 * UPS,
	.t4n = 8 * UPS,
	.t4e = UPS / 2,
	.t5 = UPS / 10,
	.t6 = 4 * UPS,
	.t7 = 1 * UPS,
	.rb_abate = 3,
	.rb_accept = 6,
	.rb_discard = 9,
	.tb_abate_1 = 128 * 272,
	.tb_onset_1 = 256 * 272,
	.tb_discd_1 = 384 * 272,
	.tb_abate_2 = 512 * 272,
	.tb_onset_2 = 640 * 272,
	.tb_discd_2 = 768 * 272,
	.tb_abate_3 = 896 * 272,
	.tb_onset_3 = 1024 * 272,
	.tb_discd_3 = 1152 * 272,
	.N1 = 127,
	.N2 = 8192,
	.M = 5,
};

STATIC sl_config_t sl_default_j1_chan = {
	.t1 = 45 * UPS,
	.t2 = 5 * UPS,
	.t2l = 20 * UPS,
	.t2h = 100 * UPS,
	.t3 = 1 * UPS,
	.t4n = 8 * UPS,
	.t4e = UPS / 2,
	.t5 = UPS / 10,
	.t6 = 4 * UPS,
	.t7 = 1 * UPS,
	.rb_abate = 3,
	.rb_accept = 6,
	.rb_discard = 9,
	.tb_abate_1 = 128 * 272,
	.tb_onset_1 = 256 * 272,
	.tb_discd_1 = 384 * 272,
	.tb_abate_2 = 512 * 272,
	.tb_onset_2 = 640 * 272,
	.tb_discd_2 = 768 * 272,
	.tb_abate_3 = 896 * 272,
	.tb_onset_3 = 1024 * 272,
	.tb_discd_3 = 1152 * 272,
	.N1 = 127,
	.N2 = 8192,
	.M = 5,
};

STATIC sl_config_t sl_default_j1_span = {
	.t1 = 45 * UPS,
	.t2 = 5 * UPS,
	.t2l = 20 * UPS,
	.t2h = 100 * UPS,
	.t3 = 1 * UPS,
	.t4n = 8 * UPS,
	.t4e = UPS / 2,
	.t5 = UPS / 10,
	.t6 = 4 * UPS,
	.t7 = 1 * UPS,
	.rb_abate = 3,
	.rb_accept = 6,
	.rb_discard = 9,
	.tb_abate_1 = 128 * 272,
	.tb_onset_1 = 256 * 272,
	.tb_discd_1 = 384 * 272,
	.tb_abate_2 = 512 * 272,
	.tb_onset_2 = 640 * 272,
	.tb_discd_2 = 768 * 272,
	.tb_abate_3 = 896 * 272,
	.tb_onset_3 = 1024 * 272,
	.tb_discd_3 = 1152 * 272,
	.N1 = 127,
	.N2 = 8192,
	.M = 5,
};

STATIC sdt_config_t sdt_default_e1_span = {
	.Tin = 4,
	.Tie = 1,
	.T = 64,
	.D = 256,
	.t8 = UPS / 10,
	.Te = 793544,
	.De = 11328,
	.Ue = 198384,
	.N = 16,
	.m = 272,
	.b = 8,
	.f = SDT_FLAGS_ONE,
};

STATIC sdt_config_t sdt_default_t1_span = {
	.Tin = 4,
	.Tie = 1,
	.T = 64,
	.D = 256,
	.t8 = UPS / 10,
	.Te = 577169,
	.De = 9308,
	.Ue = 144292,
	.N = 16,
	.m = 272,
	.b = 8,
	.f = SDT_FLAGS_ONE,
};

STATIC sdt_config_t sdt_default_j1_span = {
	.Tin = 4,
	.Tie = 1,
	.T = 64,
	.D = 256,
	.t8 = UPS / 10,
	.Te = 577169,
	.De = 9308,
	.Ue = 144292,
	.N = 16,
	.m = 272,
	.b = 8,
	.f = SDT_FLAGS_ONE,
};

STATIC sdt_config_t sdt_default_e1_chan = {
	.Tin = 4,
	.Tie = 1,
	.T = 64,
	.D = 256,
	.t8 = UPS / 10,
	.Te = 793544,
	.De = 11328,
	.Ue = 198384,
	.N = 16,
	.m = 272,
	.b = 8,
	.f = SDT_FLAGS_ONE,
};

STATIC sdt_config_t sdt_default_t1_chan = {
	.Tin = 4,
	.Tie = 1,
	.T = 64,
	.D = 256,
	.t8 = UPS / 10,
	.Te = 577169,
	.De = 9308,
	.Ue = 144292,
	.N = 16,
	.m = 272,
	.b = 8,
	.f = SDT_FLAGS_ONE,
};

STATIC sdt_config_t sdt_default_j1_chan = {
	.Tin = 4,
	.Tie = 1,
	.T = 64,
	.D = 256,
	.t8 = UPS / 10,
	.Te = 577169,
	.De = 9308,
	.Ue = 144292,
	.N = 16,
	.m = 272,
	.b = 8,
	.f = SDT_FLAGS_ONE,
};

STATIC sdl_config_t sdl_default_e1_chan = {
	.ifname = NULL,
	.ifflags = 0,
	.iftype = SDL_TYPE_DS0,
	.ifrate = 64000,
	.ifgtype = SDL_GTYPE_E1,
	.ifgrate = 2048000,
	.ifmode = SDL_MODE_IDLE,
	.ifgmode = SDL_GMODE_NONE,
	.ifgcrc = SDL_GCRC_CRC4,
	.ifclock = SDL_CLOCK_SLAVE,
	.ifcoding = SDL_CODING_HDB3,
	.ifframing = SDL_FRAMING_CCS,
	.ifblksize = 8,
	.ifleads = 0,
	.ifbpv = 0,
	.ifalarms = 0,
	.ifrxlevel = 0,
	.iftxlevel = 1,
	.ifsync = 0,
	.ifsyncsrc = {0, 0, 0, 0}
};

STATIC sdl_config_t sdl_default_t1_chan = {
	.ifname = NULL,
	.ifflags = 0,
	.iftype = SDL_TYPE_DS0,
	.ifrate = 64000,
	.ifgtype = SDL_GTYPE_T1,
	.ifgrate = 1544000,
	.ifmode = SDL_MODE_IDLE,
	.ifgmode = SDL_GMODE_NONE,
	.ifgcrc = SDL_GCRC_CRC6,
	.ifclock = SDL_CLOCK_LOOP,
	.ifcoding = SDL_CODING_B8ZS,
	.ifframing = SDL_FRAMING_ESF,
	.ifblksize = 8,
	.ifleads = 0,
	.ifbpv = 0,
	.ifalarms = 0,
	.ifrxlevel = 0,
	.iftxlevel = 0,
	.ifsync = 0,
	.ifsyncsrc = {0, 0, 0, 0}
};

STATIC sdl_config_t sdl_default_j1_chan = {
	.ifname = NULL,
	.ifflags = 0,
	.iftype = SDL_TYPE_DS0A,
	.ifrate = 64000,
	.ifgtype = SDL_GTYPE_J1,
	.ifgrate = 1544000,
	.ifmode = SDL_MODE_IDLE,
	.ifgmode = SDL_GMODE_NONE,
	.ifgcrc = SDL_GCRC_CRC6J,
	.ifclock = SDL_CLOCK_LOOP,
	.ifcoding = SDL_CODING_B8ZS,
	.ifframing = SDL_FRAMING_ESF,
	.ifblksize = 8,
	.ifleads = 0,
	.ifbpv = 0,
	.ifalarms = 0,
	.ifrxlevel = 0,
	.iftxlevel = 0,
	.ifsync = 0,
	.ifsyncsrc = {0, 0, 0, 0}
};

STATIC sdl_config_t sdl_default_e1_span = {
	.ifname = NULL,
	.ifflags = 0,
	.iftype = SDL_TYPE_E1,
	.ifrate = 2048000,
	.ifgtype = SDL_GTYPE_E1,
	.ifgrate = 2048000,
	.ifmode = SDL_MODE_PEER,
	.ifgmode = SDL_GMODE_NONE,
	.ifgcrc = SDL_GCRC_CRC4,
	.ifclock = SDL_CLOCK_SLAVE,
	.ifcoding = SDL_CODING_HDB3,
	.ifframing = SDL_FRAMING_CCS,
	.ifblksize = 64,
	.ifleads = 0,
	.ifbpv = 0,
	.ifalarms = 0,
	.ifrxlevel = 0,
	.iftxlevel = 1,
	.ifsync = 0,
	.ifsyncsrc = {0, 0, 0, 0}
};

STATIC sdl_config_t sdl_default_t1_span = {
	.ifname = NULL,
	.ifflags = 0,
	.iftype = SDL_TYPE_T1,
	.ifrate = 1544000,
	.ifgtype = SDL_GTYPE_T1,
	.ifgrate = 1544000,
	.ifmode = SDL_MODE_PEER,
	.ifgmode = SDL_GMODE_NONE,
	.ifgcrc = SDL_GCRC_CRC6,
	.ifclock = SDL_CLOCK_LOOP,
	.ifcoding = SDL_CODING_B8ZS,
	.ifframing = SDL_FRAMING_ESF,
	.ifblksize = 64,
	.ifleads = 0,
	.ifbpv = 0,
	.ifalarms = 0,
	.ifrxlevel = 0,
	.iftxlevel = 0,
	.ifsync = 0,
	.ifsyncsrc = {0, 0, 0, 0}
};

STATIC sdl_config_t sdl_default_j1_span = {
	.ifname = NULL,
	.ifflags = 0,
	.iftype = SDL_TYPE_J1,
	.ifrate = 1544000,
	.ifgtype = SDL_GTYPE_J1,
	.ifgrate = 1544000,
	.ifmode = SDL_MODE_PEER,
	.ifgmode = SDL_GMODE_NONE,
	.ifgcrc = SDL_GCRC_CRC6J,
	.ifclock = SDL_CLOCK_LOOP,
	.ifcoding = SDL_CODING_B8ZS,
	.ifframing = SDL_FRAMING_ESF,
	.ifblksize = 64,
	.ifleads = 0,
	.ifbpv = 0,
	.ifalarms = 0,
	.ifrxlevel = 0,
	.iftxlevel = 0,
	.ifsync = 0,
	.ifsyncsrc = {0, 0, 0, 0}
};

/** xp_allocate_chans: - allocate or reinitialize channel structures for a span
  * @sp: span structure pointer
  *
  * Allocates (on initial config or T1/J1 to E1 change-up) or reinitializes (on T1/J1 E1 change) the
  * channel structures for a given span.  Once channels have been allocated, this function cannot
  * fail.  Note that 31 channel structures are allocated regardless of span type.
  */
noinline __unlikely int
xp_allocate_chans(struct sp *sp)
{
	int chan;

	for (chan = 0; chan < 32; chan++) {
		struct ch *ch;

		if (!(ch = sp->chans[chan])) {
			if (!(ch = xp_alloc_ch(sp, chan)))
				return (-ENOMEM);
		} else {
			if (ch->sdl.config.ifgtype != sp->config.ifgtype)
				xp_init_ch(sp, ch, chan);
		}
	}
	return (0);
}

noinline __unlikely int xp_t1_span_config(struct sp *sp, bool timeouts);
noinline __unlikely int xp_e1_span_config(struct sp *sp, bool timeouts);
noinline __unlikely int xp_x1_span_config(struct sp *sp, bool timeouts);

/** xp_span_config: - perform initial configuration on a span
  * @sp: span structure pointer
  * @timeouts: perform initial boot timeouts
  */
noinline __unlikely int
xp_span_config(struct sp *sp, bool timeouts)
{
	int err;

	switch (sp->cd->device) {
	case XP_DEV_DS21352:
	case XP_DEV_DS21552:
		err = xp_t1_span_config(sp, timeouts);
		break;
	case XP_DEV_DS21354:
	case XP_DEV_DS21554:
		err = xp_e1_span_config(sp, timeouts);
		break;
	case XP_DEV_DS2155:
	case XP_DEV_DS21455:
	case XP_DEV_DS21458:
		err = xp_x1_span_config(sp, timeouts);
		break;
	default:
		swerr();
		err = -EFAULT;
		break;
	}
	return (err);
}

noinline __unlikely void xp_t1_chan_config(struct sp *sp, struct ch *ch, volatile uint8_t *xlb);

noinline __unlikely int
xp_t1_span_config(struct sp *sp, bool timeouts)
{
	volatile unsigned char *xlb = (typeof(xlb)) sp->iobase;
	u_char tcr1 = 0, ccr2 = 0, ccr3 = 0, ccr5 = 0, ccr6 = 0, licr = 0, test2 = 0;
	int offset;
	int err;

	if ((err = xp_allocate_chans(sp)) != 0)
		return (err);

	/* set the idle code */
	xlb[0x3f] = 0x7f;	/* TIDR */
	for (offset = 0; offset < 8; offset++) {
		xlb[0x50 + offset] = 0x7f;	/* TC1 to TC8 */
	}
	for (offset = 0; offset < 16; offset++) {
		xlb[0x40 + offset] = 0x7f;	/* TC9 to TC24 */
		xlb[0x80 + offset] = 0x7f;	/* RC1 to RC16 */
	}
	for (offset = 0; offset < 8; offset++) {
		xlb[0x58 + offset] = 0x7f;	/* RC17 to RC24 */
	}

	switch (sp->config.ifclock) {
	default:
		sp->config.ifclock = SDL_CLOCK_LOOP;
	case SDL_CLOCK_LOOP:
		/* Use the signal present at RCLK as the transmit clock.  The TCLK pin is ignored. */
		ccr3 |= (1 << 6);	/* CCR3.6: TCLKSRC: Transmit Clock Source Select. 0 = TCR1.7; 1 =
					   RCLK. */
		tcr1 &= ~(1 << 7);	/* TCR1.7: LOTCMC: Loss of Transmit Clock Mux Control. 0 = TCLK; 1 =
					   RCLK if TCLK stops. */
		/* default for T1 in tor3 driver, and here */
		break;
	case SDL_CLOCK_INT:
		/* The TCLK pin is always the source of transmit clock. */
		ccr3 &= ~(1 << 6);	/* CCR3.6: TCLKSRC: Transmit Clock Source Select. 0 = TCR1.7; 1 =
					   RCLK. */
		tcr1 &= ~(1 << 7);	/* TCR1.7: LOTCMC: Loss of Transmit Clock Mux Control. 0 = TCLK; 1 =
					   RCLK if TCLK stops. */
		break;
	case SDL_CLOCK_MASTER:
		/* Use the scaled signal present at MCLK as the transmit clock. The TCLK pin is ignored. */
		ccr3 &= ~(1 << 6);	/* CCR3.6: TCLKSRC: Transmit Clock Source Select. 0 = TCR1.7; 1 =
					   RCLK. */
		tcr1 &= ~(1 << 7);	/* TCR1.7: LOTCMC: Loss of Transmit Clock Mux Control. 0 = TCLK; 1 =
					   RCLK if TCLK stops. */
		break;
	case SDL_CLOCK_EXT:
		/* Use the scaled signal present at TSYSCLK as the transmit clock.  The TCLK pin is ignored. */
		ccr3 &= ~(1 << 6);	/* CCR3.6: TCLKSRC: Transmit Clock Source Select. 0 = TCR1.7; 1 =
					   RCLK. */
		tcr1 &= ~(1 << 7);	/* TCR1.7: LOTCMC: Loss of Transmit Clock Mux Control. 0 = TCLK; 1 =
					   RCLK if TCLK stops. */
		break;
	case SDL_CLOCK_SLAVE:
		/* Switch to the clock present at RCLK when the signal at the TCLK pin fails to transition
		   after 1 channel time. */
		ccr3 &= ~(1 << 6);	/* CCR3.6: TCLKSRC: Transmit Clock Source Select. 0 = TCR1.7; 1 =
					   RCLK. */
		tcr1 |= (1 << 7);	/* TCR1.7: LOTCMC: Loss of Transmit Clock Mux Control. 0 = TCLK; 1 =
					   RCLK if TCLK stops. */
		/* default for E1 in tor3 driver, and here */
		break;
	}
	switch (sp->config.ifframing) {
	default:
		sp->config.ifframing = SDL_FRAMING_ESF;
		/* fall through */
	case SDL_FRAMING_ESF:
		/* CCR2.7: TFM: Transmit Frame Mode Select. 0 = D4; 1 = ESF. */
		ccr2 |= (1 << 7);
		/* CCR2.3: RFM: Receive Frame Mode Select. 0 = D4; 1 = ESF. */
		ccr2 |= (1 << 3);
		break;
	case SDL_FRAMING_SF:
		/* CCR2.5: TSLC96: Transmit SLC-96/Fs-Bit Insertion. 0 = disabled; 1 = enabled. */
		ccr2 |= (1 << 5);
		break;
	}
	switch (sp->config.ifcoding) {
	default:
		sp->config.ifcoding = SDL_CODING_B8ZS;
		/* fall through */
	case SDL_CODING_B8ZS:
		/* CCR2.6: TB7ZS: Transmit B8ZS Enable. 0 = disabled; 1 = enabled. */
		ccr2 |= (1 << 6);
		/* CCR2.2: RB8ZS: Receive B8ZS Enable. 0 = disabled; 1 = enabled. */
		ccr2 |= (1 << 2);
		break;
	case SDL_CODING_AMI:
		xlb[0x7e] = 0x1c;	/* Set FDL register to 0x1c */
		break;
	}
	switch (sp->config.ifgcrc) {
	default:
		switch (sp->config.ifgtype) {
		default:
			sp->config.ifgtype = SDL_GTYPE_T1;
			/* fall through */
		case SDL_GTYPE_T1:
			sp->config.ifgcrc = SDL_GCRC_CRC6;
			goto gcrc_crc6;
		case SDL_TYPE_J1:
			sp->config.ifgcrc = SDL_GCRC_CRC6J;
			goto gcrc_crc6j;
		}
		break;
	case SDL_GCRC_CRC6:
	      gcrc_crc6:
		/* CCR5.7: TJC: 0 = ANSI CRC6 Tx, 1 = JT-G704 CRC6 Tx */
		ccr5 &= ~(1 << 7);
		/* CRC6.7: RJC: 0 = ANSI CRC6 Rx, 1 = JT-G704 CRC6 Rx */
		ccr6 &= ~(1 << 7);
		break;
	case SDL_GCRC_CRC6J:
	      gcrc_crc6j:
		/* CCR5.7: TJC: 0 = ANSI CRC6 Tx, 1 = JT-G704 CRC6 Tx */
		ccr5 |= (1 << 7);
		/* CRC6.7: RJC: 0 = ANSI CRC6 Rx, 1 = JT-G704 CRC6 Rx */
		ccr6 |= (1 << 7);
		break;
	}
	if (sp->config.iftxlevel < 8) {
		/* not monitoring mode */
		licr &= ~(1 << 0);	/* 0dB CSU, transmitters on */
		licr |= ((sp->config.iftxlevel & 0x7) << 5);	/* LBO */
	} else {
		/* monitoring mode */
		licr |= (1 << 0);	/* 0dB CSU, transmitters off */
		switch (sp->config.iftxlevel & 0x3) {
		case 1:
			test2 |= 0x72;	/* TEST2 12dB gain */
			/* 150 Ohm Lo-Z tap */
			break;
		case 2:
		case 3:
			test2 |= 0x70;	/* TEST2 20db gain */
			/* single 432 OHm Hi-Z tap */
			break;
		}
	}

	xlb[0x2b] = 0x08;	/* Full-on sync required (RCR1) */
	/* RCR1.7: LCVCRF: Line Code Violation Count Register Function Select.  0 = do not count excessive
	   zeros; 1 = count excessive zeros. */
	/* RCR1.6: ARC: Auto Resync Criteria. 0 = resync on OOF or RCL; 1 = resync on OOF only */
	/* RCR1.5: OOF1: Out of Frame Select 1.  0 = 2/4 fram bits in error; 1 = 2/5 frame bits in error. */
	/* RCR1.4: OOF2: Out of Frame Select 2.  0 = follor RCR1.5; 1 = 2/6 frame bits in error */
	/* RCR1.3: SYNCC: Sync Criteria.  D4 0 = search Ft then Fs; 1 = cross couple Ft and Fs. ESF 0 =
	   search for FPS only; 1 = search for FPS and verify with CRC6 */
	/* RCR1.2: SYNCT: Sync Time.  0 = qualify 10 bits; 1 = qualify 24 bits. */
	/* RCR1.1: SYNCE: Sync Enable. 0 = auto resync enabled; 1 = auto resync disabled. */
	/* RCR1.0: RESYNC: Resync. Toggle to request a resync. */

	xlb[0x2c] = (1 << 3) | (1 << 7);	/* RSYNC is an input (RCR2) */
	/* RCR2.7: RCS: Receive Code Select. 0 = idle code (0x7f); 1 = milliwatt. */
	/* RCR2.6: RZBTSI: Rx ZBTSI support. 0 = disabled; 1 = enabled. */
	/* RCR2.5: RSDW: RSYNC double-wide. 0 = single wide; 1 = double wide. */
	/* RCR2.4: RSM: RSYNC Mode Select. 0 = frame mode; 1 = multiframe mode. */
	/* RCR2.3: RSIO: RSYNC I/O Select. 0 = ouput; 1 = input. */
	/* RCR2.2: RD4YM: Receive Side D4 Yellow Alarm Select. 0 = zeros in bit 2; 1 = one in S-bit frame 12. 
	 */
	/* RCR2.1: FSBE: PCVCR Rs-Bit Error Report Enable. 0 = no report Fs-bit errors; 1 = report Fs-bit
	   errors. */
	/* RCR2.0: MOSCRF: Multiframe out-of-sync Count Register Function Select.  0 = framing bits; 1 =
	   number of multiframes. */

	xlb[0x35] = tcr1 | (1 << 4);	/* RBS enable (TCR1) */
	/* TCR1.7=X: LOTCMC: Loss of Transmit Clock Mux Control. 0 = TCLK; 1 = RCLK if TCLK stops. */
	/* TCR1.6=0: TFPT: Transmit F-bit pass through. 0 = F bits source internal; 1 = sampled at TSER. */
	/* TCR1.5=0: TCPT: Transmit CRC pass through. 0 = CRC source internal; 1 = sampled at TSER. */
	/* TCR1.4=1: TSSE: Transmit Software Signalling Enable. 0 = no signalling; 1 = from TSx/TTRx */
	/* TCR1.3=0: GB7S: Global Bit 7 Stuffing. 0 = by TTR regs; 1 = forced */
	/* TCR1.2=0: TFDLS: TFDL Register Select. 0 = FDL or Fs source internal; 1 = from HDLC/BOC or TLINK
	   pin. */
	/* TCR1.1=0: TBL: Transmit Blue Alarm. 0 = normal; 1 = tx unframed all ones. */
	/* TCR1.0=0: TYEL: Transmit Yellow Alarm. 0 = no alarm; 1 = yellow alarm. */

	xlb[0x36] = 0x04;	/* TSYNC to be output (TCR2) */
	/* TCR2.7: TEST1: Test mode. 0 = normal operation. */
	/* TCR2.6: TEST0: Test mode. 0 = normal operation. */
	/* TCR2.5: TZBTSI: Tx ZBTSI Support. 0 = disabled; 1 = enabled. */
	/* TCR2.4: TSDDW: TSYN double-wide. 0 = single wide; 1 = double wide. */
	/* TCR2.3: TSM: TSYN Mode Select. 0 = frame mode; 1 = multiframe mode. */
	/* TCR2.2: TSIO: TSYNC I/O Select. 0 = input; 1 = output. */
	/* TCR2.1: TD4YM: Tx D4 Yellow Alarm Select. 0 = bit2 all chan; 1 = S-bit frame 12. */
	/* TCR2.0: TB7ZS: Tx Bit 7 Zero Suppression. 0 = no stuffing; 1 = force bit 7. */

	xlb[0x37] = 0x9c;	/* Tx & Rx Elastic stor, sysclk(s) = 2.048 mhz, loopback controls (CCR1) */
	/* CCR1.7: TESE: Transmit Elastic Store Enable. 0 = bypassed; 1 = enabled. */
	/* CCR1.6: ODF: Output Data Format. 0 = bipolar; 1 = NRZ. */
	/* CCR1.5: RSAO: Receive Signalling All Ones. 0 = sign at RSER; 1 = forced to one. */
	/* CCR1.4: TSCLKM: TSYSCLK Mode Select. 0 = 1.544 MHz; 1 = 2.048 or IBO. */
	/* CCR1.3: RSCLKM: RSYSCLK Mode Select. 0 = 1.544 MHz; 1 = 2.048 or IBO. */
	/* CCR1.2: RESE: Receive Elastic Store Enable. 0 = bypassed; 1 = enabled. */
	/* CCR1.1: PLB: Payload Loopback. 0 = disabled; 1 = enabled. */
	/* CCR1.0: FLB: Framer Loopback. 0 = disabled; 1 = enabled. */

	xlb[0x38] = ccr2;
	/* CCR2.7: TFM: Transmit Frame Mode Select. 0 = D4; 1 = ESF. */
	/* CCR2.6: TB7ZS: Transmit B8ZS Enable. 0 = disabled; 1 = enabled. */
	/* CCR2.5: TSLC96: Transmit SLC-96/Fs-Bit Insertion. 0 = disabled; 1 = enabled. */
	/* CCR2.4: TFDL: Transmit FDL Zero Stuffer Enable. 0 = diabled; 1 = enabled. */
	/* CCR2.3: RFM: Receive Frame Mode Select. 0 = D4; 1 = ESF. */
	/* CCR2.2: RB8ZS: Receive B8ZS Enable. 0 = disabled; 1 = enabled. */
	/* CCR2.1: RSLC96: Receive SLC-96 Enable. 0 = disabled; 1 = enabled. */
	/* CCR2.0: RZSE: Receive FDL Zero Destuffer Enable. 0 = disabled; 1 = enabled. */

	xlb[0x30] = ccr3 | (1 << 2);
	/* CCR3.7: RESMDM: Rx Elastic Store Min Delay. 0 = full; 1 = 32-bits. */
	/* CCR3.6: TCLKSRC: Transmit Clock Source Select. 0 = TCR1.7; 1 = RCLK. */
	/* CCR3.5: RLOSF: RLOS/LOTC output. 0 = RLOS; 1 = LOTC. */
	/* CCR3.4: RSMS: RSYNC MF Skip. 0 = each; 1 = every other. */
	/* CCR3.3: PDE: Pulse Density Enforcer Enable. 0 = disable; 1 = enable. */
	/* CCR3.2: ECUS: Error Counter Update. 0 = one second; 1 = 42 ms (333 frames). */
	/* CCR3.1: TLOOP: Transmit Loop Code Enable. 0 = normal data; 1 = TCD data. */
	/* CCR3.0: TESMDM: Tx Elastic Store Min Delay. 0 = full; 1 = 32-bits. */

	xlb[0x11] = (1 << 0);
	/* CCR4.7: RSRE: Rx Sign. Reinsertion. 0 = disabled; 1 = reinsert. */
	/* CCR4.6: RPCSI: Rx Perchannel Sign Insert. 0 = disabled; 1 = enabled. */
	/* CCR4.5: RFSA1: Receive Force Sign. All Ones. 0 = normal; 1 = forced to 1. */
	/* CCR4.4: RFE: Receive Freeze Enable. 0 = no freeze; 1 = allow freeze. */
	/* CCR4.3: RFF: Receive Force Freeze. 0 = no freeze; 1 = freeze. */
	/* CCR4.2: THSE: Tx Hw Sign Insertion. 0 = no insert; 1 = insert. */
	/* CCR4.1: TPCSI: Tx Perchannel Sign Insert. 0 = disabled; 1 = enabled. */
	/* CCR4.0: TIRFS: Tx Idle Registers Function. 0 = idle code; 1 = loopback. */

	xlb[0x19] = ccr5;
	/* CCR5.7: TJC: Transmit Japanese CRC6 Enable. 0 = ANSI; 1 = JT-G704. */
	/* CCR5.6: LLB: Local Loopback. 0 = disabled; 1 = enabled. */
	/* CCR5.5: LIAIS: Lin Interface ASI Generation Enable. 0 = normal; 1 = unframed all ones. */
	/* CCR5.4-0: TCM4-TCM0: Transmit Monitor channel. */

	xlb[0x1e] = ccr6;
	/* CCR6.7: RJC: Receive Japanese CRC6 Enable. 0 = ANSI; 1 = JT-G704. */
	/* CCR6.6: RESA: Receive Elastic Store Align. toggle to align. */
	/* CCR6.5: TESA: Transmit Elastic Store Align. toggle to align. */
	/* CCR6.4-0: RSM4-RSM0: Receive Monitor channel. */

	xlb[0x0a] = 0x00;
	/* CCR7.7: LIRST: Line Interface Reset.  Toggle and wait 40ms to reset. */
	/* CCR7.6: RLB: Remote Loopback. 0 = disabled; 1 = enabled. */
	/* CCR7.5: RESR: Receive Elastic Store Reset.  toggle to reset. */
	/* CCR7.4: TESR: Transmit Elastic Store Reset.  toggle to reset. */
	/* CCR7.3: -: Reserved.  Set to zero. */
	/* CCR7.2: LIUSI: Line Interface Sync Interface Enable. 0 = T1 signal; 1 = Sync signal. */
	/* CCR7.1: CDIG: Customer Disconnect Indication Generator. 0 = normal; 1 = CDI. */
	/* CCR7.0: LIUODO: Line Interface Open Drain Option. 0 = normal; 1 = open drain. */

	xlb[0x7f] = 0xff;
	/* IMR1.7: LUP: Loop Up Code Detect. */
	/* IMR1.6: LDN: Loop Down Code Detect. */
	/* IMR1.5: LOTC: Loss of Transmit Clock. */
	/* IMR1.4: SLIP: Elastic Store Slip Occurrence. */
	/* IMR1.3: RBL: Receive Blue Alarm. */
	/* IMR1.2: RYEL: Receive Yelllow Alarm. */
	/* IMR1.1: LRCL: Line Interface Receive Carrier Loss. */
	/* IMR1.0: RLOS: Receive Loss of Sync. */

	xlb[0x6f] = 0x20;
	/* IMR2.7: RMF: Receive Multiframe. */
	/* IMR2.6: TMF: Transmit Multiframe. */
	/* IMR2.5: SEC: One Second Timer. */
	/* IMR2.4: RFDL: Receive FDL Buffer Full. */
	/* IMR2.3: TFDL: Transmit FDL Buffer Empty. */
	/* IMR2.2: RMTCH: Receive FDL Match Occurrence. */
	/* IMR2.1: RAF: Receive FDL Abort. */
	/* IMR2.0: RSC: Receive Signaling Change. */

	xlb[0x12] = 0x22;	/* IBCC 5-bit loop up, 3-bit loop down code */
	xlb[0x13] = 0x80;	/* TCD - 10000 */
	xlb[0x14] = 0x80;	/* RUPCD - 10000 */
	xlb[0x15] = 0x80;	/* RDNCD - 100 */

	xlb[0x09] = test2;	/* TEST2 */

	xlb[0x7c] = licr;
	/* LICR.7-5: L2-L0: Line Build Out Select. */
	/* LICR.4: EGL: Receive Equalizer Gain Limit. 0 = -36dB, 1 = -30dB. */
	/* LICR.3: JAS: Jitter Attenuator Select. 0 = receive; 1 = transmit. */
	/* LICR.2: JABDS: Jitter Attenuator Buffer Depth Select. 0 = 128 bits; 1 = 32 bits. */
	/* LICR.1: DJA: Disable Jitter Attenuator. 0 = enabled; 1 = disabled. */
	/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */

	/* CONFIGURE ALL TIMESLOTS. */
	{
		struct ch *ch;
		int chan;

		for (chan = 1; chan < 25; chan++) {
			if ((ch = sp->chans[chan]) != NULL) {
				xp_t1_chan_config(sp, ch, xlb);
				continue;
			}
			swerr();
		}
	}

	/* SET UP INTERRUPT MASK REGISTERS */

	xlb[0x7f] = 0xff;
	/* IMR1.7: LUP: Loop Up Code Detected. */
	/* IMR1.6: LDN: Loop Down Code Detected. */
	/* IMR1.5: LOTC: Loss of Transmit Clock. */
	/* IMR1.4: SLIP: Elastic Store Slip Occurrence. */
	/* IMR1.3: RBL: Receive Blue Alarm. */
	/* IMR1.2: RYEL: Receive Yellow Alarm. */
	/* IMR1.1: LRCL: Line Interface Receive Carrier Loss. */
	/* IMR1.0: RLOS: Receive Loss of Sync. */

	xlb[0x6f] = (1 << 5);
	/* IMR2.7: RMF: Receive Multiframe. */
	/* IMR2.6: TMF: Transmit Multiframe. */
	/* IMR2.5: SEC: One Second Timer. */
	/* IMR2.4: RFDL: Receive FDL Buffer Full. */
	/* IMR2.3: TFDL: Transmit FDL Buffer Empty. */
	/* IMR2.2: RMTCH: Receive FDL Match Occurrence. */
	/* IMR2.1: RAF: Receive FDL Abort. */
	/* IMR2.0: RSC: Receive Signaling Change. */

	if (timeouts) {
		unsigned long timeout;

		/* line interface reset */
		xlb[0x0a] |= (1 << 7);
		xlb[0x0a] &= ~(1 << 7);

		/* wait for 40 ms */
		timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
		while ((volatile unsigned long)
		       jiffies < timeout) ;

		/* elastic store reset */
		/* CCR7.4 and CCR7.5 are TESR and RESR (resp) */
		xlb[0x0a] |= ((1 << 4) | (1 << 5));	/* CCR7: TESR and RESR */
		xlb[0x0a] &= ~((1 << 4) | (1 << 5));	/* CCR7: */
		/* elastic store align */
		/* CCR6.5 and CCR6.6 are TESA and RESA (resp) */
		xlb[0x1e] |= ((1 << 5) | (1 << 6));	/* CCR6: TESA and RESA */
		xlb[0x1e] &= ~((1 << 5) | (1 << 6));	/* CCR6: */

	}
	return (0);
}

/** xp_t1_chan_config: - configure a T1/J1 channel
  * @sp: span structure pointer
  * @ch: chan structure pointer
  */
noinline __unlikely void
xp_t1_chan_config(struct sp *sp, struct ch *ch, volatile uint8_t *xlb)
{
	if (ch->chan == 0) {
		swerr();
		return;
	}

	if (ch->sdl.config.iftype != SDL_TYPE_DS0A)
		/* DS0 needs to be set for clear-channel operation. */
		xlb[0x39 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
	else
		/* DS0A does not need to be set for clear-channel operation. */
		xlb[0x39 + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));

	/* Per-channel quiet code. */
	if (ch->sdl.config.ifmode == SDL_MODE_IDLE) {
		xlb[0x16 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
		xlb[0x1b + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
	} else {
		xlb[0x16 + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
		xlb[0x1b + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
	}

	/* Per-channel milliwatt. */
	/* RCR2.7 must be 1 for this to work. */
	if (ch->sdl.config.ifmode == SDL_MODE_MW) {
		xlb[0x16 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
		xlb[0x2d + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
	} else
		xlb[0x2d + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));

	/* Per-channel remote payload loopback. */
	/* CCR4.0 must be 1 for this to work. */
	if (ch->sdl.config.ifmode == SDL_MODE_REM_LB)
		xlb[0x3c + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
	else
		xlb[0x3c + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
}

noinline __unlikely void xp_e1_chan_config(struct sp *sp, struct ch *ch, volatile uint8_t *xlb);

noinline __unlikely int
xp_e1_span_config(struct sp *sp, bool timeouts)
{
	volatile unsigned char *xlb = (typeof(xlb)) sp->iobase;
	uint8_t ccr1 = 0, tcr1 = 0, tcr2 = 0, licr = 0, test3 = 0, ccr6 = 0, ccr2 = 0;
	int offset;
	int err;

	if ((err = xp_allocate_chans(sp)) != 0)
		return (err);

	/* The 2.048 MHz clock attached at the MCLK pin is internally multiplied by 16 via an internal PLL
	   circuit to form a 16-times over-sampler, which is used to recover the clock and data.

	   Normally the clock that is output at the RCLKO pin is the recovered clock from the E1 AMI/HDB3
	   waveform presented at the RTIP and RRING inputs.  When no AMI signal is present at RTIP and RRING, 
	   a receive carrier loss (RCL) condition occurs, and the RCLKO is sourced from the clock applied at
	   the MCLK pin.  If the jitter attenuator is either placed in the transmit path or disabled, the
	   RCLKO output can exhibit slightly shorter high cycles of the clock, which is due to the highly
	   oversampled digital clock recovery circuitry.  if the jitter attenuator is placed in the receive
	   path (as is the case in most applications), the jitter attenuator restores the RCLK to being close 
	   to 50% duty cycle. */

	switch (sp->config.ifclock) {
	default:
		sp->config.ifclock = SDL_CLOCK_LOOP;
	case SDL_CLOCK_LOOP:
		/* Use the signal present at RCLK as the transmit clock.  The TCLK pin is ignored. */
		ccr6 |= (1 << 2);	/* CCR6.2: 1 = Force transmitter to internally switch to RCLK as
					   source of transmit clock. Signal at TCLK pin is ignored. */
		ccr2 &= ~(1 << 2);	/* CCR2.2: 0 = do not switch to RCLKO if TCLK stops */
		break;
	case SDL_CLOCK_INT:
		/* The TCLK pin is always the source of the transmit clock. */
		ccr6 &= ~(1 << 2);	/* CCR6.2: 0 = source to transmit clock determined by CCR2.2
					   (LOTCMC). */
		ccr2 &= ~(1 << 2);	/* CCR2.2: 0 = do not switch to RCLKO if TCLK stops */
		break;
	case SDL_CLOCK_MASTER:
		/* Use the scaled signal present at MCLK as the transmit clock. The TCLK pin is ignored. */
		ccr6 &= ~(1 << 2);	/* CCR6.2: 0 = source to transmit clock determined by CCR2.2
					   (LOTCMC). */
		ccr2 &= ~(1 << 2);	/* CCR2.2: 0 = do not switch to RCLKO if TCLK stops */
		break;
	case SDL_CLOCK_EXT:
		/* Use the scaled signal present at the TSYSCLK as the transmit clock. The TCLK pin is
		   ignored. */
		ccr6 &= ~(1 << 2);	/* CCR6.2: 0 = source to transmit clock determined by CCR2.2
					   (LOTCMC). */
		ccr2 &= ~(1 << 2);	/* CCR2.2: 0 = do not switch to RCLKO if TCLK stops */
		break;
	case SDL_CLOCK_SLAVE:
		/* Switch to the clock present at RCLK when the signal at the TCLK pin fails to transition
		   after 1 channel time. */
		ccr6 &= ~(1 << 2);	/* CCR6.2: 0 = source to transmit clock determined by CCR2.2
					   (LOTCMC). */
		ccr2 |= (1 << 2);	/* CCR2.2: 1 = switch to RCLKO if TCLK stops */
		break;
	}
	switch (sp->config.ifframing) {
	default:
		sp->config.ifframing = SDL_FRAMING_CCS;
	case SDL_FRAMING_CCS:
		ccr1 |= (1 << 3);	/* CCR1.3: 1 = Rx CCS signaling mode */
		tcr1 &= ~(1 << 5);	/* TCR1.5: 0 = sample time slot 16 at TSER pin */
		break;
	case SDL_FRAMING_CAS:
		ccr1 &= ~(1 << 3);	/* CCR1.3: 0 = Rx CAS signaling mode */
		tcr1 |= (1 << 5);	/* TCR1.5: 1 = source time slot 16 from TS1 to TS15 registers */
		break;
	}
	switch (sp->config.ifcoding) {
	default:
		sp->config.ifcoding = SDL_CODING_HDB3;
	case SDL_CODING_HDB3:
		ccr1 |= (1 << 6);	/* CCR1.6: 1 = Tx HDB3 enabled */
		ccr1 |= (1 << 2);	/* CCR1.2: 1 = Rx HDB3 enabled */
		ccr2 |= (1 << 6);	/* CCR2.6: 1 = count CVs */
		break;
	case SDL_CODING_AMI:
		ccr1 &= ~(1 << 6);	/* CCR1.6: 0 = Tx HDB3 disabled */
		ccr1 &= ~(1 << 2);	/* CCR1.2: 0 = Rx HDB3 disabled */
		ccr2 &= ~(1 << 6);	/* CCR2.6: 0 = count BPVs */
		break;
	}
	switch (sp->config.ifgcrc) {
	default:
		sp->config.ifgcrc = SDL_GCRC_CRC4;
	case SDL_GCRC_CRC4:
		ccr1 |= (1 << 4);	/* CRC1.4: 1 = Tx CRC4 enabled */
		ccr1 |= (1 << 0);	/* CRC1.0: 1 = Rx CRC4 enalled */
		tcr2 |= (1 << 1);	/* TCR2.1: 1 = E-bits automatically set */
#if 0
		if (sp->config.ifframing == SDL_FRAMING_CAS)
			tcr1 |= (1 << 1);	/* TCR1.1: 1 = CAS and CRC4 multiframe mode */
#endif
		break;
	case SDL_GCRC_CRC5:
		ccr1 &= ~(1 << 4);	/* CRC1.4: 0 = Tx CRC4 disabled */
		ccr1 &= ~(1 << 0);	/* CRC1.0: 0 = Rx CRC4 disabled */
		break;
	}

	licr = 0x00;
	/* LICR.7-5: 0x0 = 75 Ohm w/o protection resistors */
	/* LICR.4: 0 = -12dB Rx EGL */
	/* LICR.3: 0 = JA on receive side */
	/* LICR.2: 0 = JA buffer depth 128 bits */
	/* LICR.1: 0 = JA enabled */
	/* LICR.0: X = transmitters on/off */
	licr = 0x10;
	/* LICR.7-5: 0x0 = 75 Ohm w/o protection resistors */
	/* LICR.4: 1 = -43dB Rx EGL */
	/* LICR.3: 0 = JA on receive side */
	/* LICR.2: 0 = JA buffer depth 128 bits */
	/* LICR.1: 0 = JA enabled */
	/* LICR.0: X = transmitters on/off */

	/* LICR.7-5: DS21354: '000' 75(1:2) Ohm normal. */
	/* LICR.7-5: DS21354: '001' 120(1:2) Ohm normal. */
	/* LICR.7-5: DS21354: '010' 75(1:2) Ohm w/ Rt */
	/* LICR.7-5: DS21354: '011' 120(1:2) Ohm w/ Rt */
	/* LICR.7-5: DS21354: '100' 75(1:2) Ohm HRL */
	/* LICR.7-5: DS21354: '101' 120(1:2) Ohm HRL */

	/* LICR.7-5: DS21554: '000' 75(1:1.15) Ohm normal */
	/* LICR.7-5: DS21554: '001' 120(1:1.15) Ohm normal */
	/* LICR.7-5: DS21554: '010' 75(1:1.15) Ohm w/ Rt */
	/* LICR.7-5: DS21554: '011' 120(1:1.15) Ohm w/ Rt */
	/* LICR.7-5: DS21554: '100' 75(1:1.15)/120(1:1.36) Ohm HLR */
	/* LICR.7-5: DS21554: '101' invalid */
	/* LICR.7-5: DS21554: '110' 75(1.1.36) Ohm HRL */
	/* LICR.7-5: DS21554: '111' invalid */

	/* Responsibility of IOCTLs to ensure that the iftxlevel has one of the valid values. Given the t2
	   and t3 circuitry, the only valid value is LICR.7-5='001' and for the DS21354 only because the
	   transformer ratios are 1:2, there are 2x60.9=120 Ohm resistors on the cards and there are no
	   transmit termination resistors (Rt) in the Tormenta design. */

	/* Theoretically, one could install 2x36 Ohm resistors instead of 2x60.9 Ohm resistors for a 75 Ohm
	   balanced termination. */

	test3 = 0x00;
	/* TEST3: 0x00 = no Rx gain */

	if (sp->config.iftxlevel < 8) {
		/* not monitoring mode */
		test3 = 0x00;	/* TEST3: 0x00 = no Rx gain */
		licr = 0x00;	/* 75 Ohm, Normal, -12dB Rx EGL, transmitter on */
		licr |= ((sp->config.iftxlevel & 0x7) << 5);	/* LBO */
	} else {
		/* monitoring mode */
		test3 = 0x00;	/* TEST3: 0x00 = no Rx gain */
		// licr = 0x01; /* LICR.0: 1 = transmitter off */
		// licr |= 0x10; /* LICR.4: 1 = -43dB Rx EGL */
		licr = 0x21;	/* 120 Ohm, Normal, -12dB Rx EGL, transmitter off */
		switch (sp->config.iftxlevel & 0x3) {
		case 0:
			break;
		case 1:
			test3 |= 0x72;	/* TEST3: 0x72 = 12dB Rx gain */
			/* 180 Ohm Lo-Z tap */
			break;
		case 2:
		case 3:
			test3 |= 0x70;	/* TEST3: 0x70 = 30dB Rx gain */
			/* 1800 Ohm Hi-Z tap */
			break;
		}
	}

	tcr1 = (1 << 3) | (1 << 0);

	xlb[0x12] = tcr1;	/* TCR1 */
	/* TCR1.7: 0 = bipolar data at TPOS0 and TNEG0 */
	/* TCR1.6: 0 = FAS bits/Sa bits/RAI sources from TAF and TNAF registers */
	/* TCR1.5: X = sample TS 16 at TSER pin/source TS 16 from TS0 to TS15 */
	/* TCR1.4: 0 = transmit data normally */
	/* TCR1.3: 1 = source Si bits from TAF and TNAF regsiters (TCR1.6 must be zero) */
	/* TCR1.2: 0 = transmit data normally */
	/* TCR1.1: X = frame mode/CAS and CRC4 multiframe mode */
	/* TCR1.0: 1 = TSYNC is an output */

	xlb[0x13] = tcr2;	/* TCR2 */
	/* TCR2.7: 0 = do not source Sa8 bit from TLINK pin */
	/* TCR2.6: 0 = do not source Sa7 bit from TLINK pin */
	/* TCR2.5: 0 = do not source Sa6 bit from TLINK pin */
	/* TCR2.4: 0 = do not source Sa5 bit from TLINK pin */
	/* TCR2.3: 0 = do not source Sa4 bit from TLINK pin */
	/* TCR2.2: 0 = TPOSO/TNEGO full clock period */
	/* TCR2.1: X = E-bits not automatically/automatically set */
	/* TCR2.0: 0 = RLOS/LOTC pin is RLOS */

	xlb[0x10] = (1 << 5);	/* RCR1 */
	/* RCR1.7: 0 = CAS/CRC4 multiframe */
	/* RCR1.6: 0 = frame mode */
	/* RCR1.5: 1 = RSYNC is an input */
	/* RCR1.4: 0 = unassigned */
	/* RCR1.3: 0 = unassigned */
	/* RCR1.2: 0 = resync if FAS received in error 3 consecutive times */
	/* RCR1.1: 0 = auto resync enabled */
	/* RCR1.0: 0 = no immediate resync */

	xlb[0x11] = (1 << 2) | (1 << 1);	/* RCR2 */
	/* RCR2.7: 0 = RLCLK low during Sa8 bit */
	/* RCR2.6: 0 = RLCLK low during Sa7 bit */
	/* RCR2.5: 0 = RLCLK low during Sa6 bit */
	/* RCR2.4: 0 = RLCLK low during Sa5 bit */
	/* RCR2.3: 0 = RLCLK low during Sa4 bit */
	/* RCR2.2: 1 = RSYSCLK is 2.048/4.096/8.192 MHz */
	/* RCR2.1: 1 = elastic store (receive) enabled */
	/* RCR2.0: 0 = unassigned */

	xlb[0x14] = ccr1;	/* CCR1 */
	/* CCR1.7: 0 = framer loopback disabled */
	/* CCR1.6: X = Tx HDB3 disabled/enabled */
	/* CCR1.5: 0 = Tx G.802 disabled */
	/* CCR1.4: X = Tx CRC4 disabled */
	/* CCR1.3: X = signalling mode CAS/CCS */
	/* CCR1.2: X = Rx HDB3 disabled/enabled */
	/* CCR1.1: 0 = Rx G.802 disabled */
	/* CCR1.0: X = Rx CRC4 disabled */

	ccr2 |= (1 << 4) | (1 << 5) | (1 << 7);

	xlb[0x1a] = ccr2;	/* CCR2 */
	/* CCR2.7: 1 = update error counters once each 62.5ms */
	/* CCR2.6: X = count CVs/count BPVs */
	/* CCR2.5: 1 = automatic transmit AIS generation enabled */
	/* CCR2.4: 1 = automatic transmit RAI generation enabled */
	/* CCR2.3: 0 = RSER as received under all conditions */
	/* CCR2.2: X = do not/do switch to RCLKO if TCLK stops */
	/* CCR2.1: 0 = do not force a freeze event */
	/* CCR2.0: 0 = no freezing of receive signalling data */

	xlb[0x1b] = (1 << 7) | (1 << 5) | (1 << 1);	/* CCR3 */
	/* CCR3.7: 1 = Tx elastic store is enabled */
	/* CCR3.6: 0 = define operation of TCHBLK output pin */
	/* CCR3.5: 1 = TIRs define channels to loopback */
	/* CCR3.4: 0 = unassigned (elastic store reset DS2154) */
	/* CCR3.3: 0 = do not reinsert signalling bits at RSER (LIRST on DS2153) */
	/* CCR3.2: 0 = do not insert signalling from TSIG pin */
	/* CCR3.1: 1 = TSYSCLK is 2.048/4.096/8.192 MHz */
	/* CCR3.0: 0 = RCL declared upon 255 consecutive zeros */

	xlb[0xa8] = 0x00;	/* CCR4 */
	/* CCR4.7: 0 = remote loopback disabled */
	/* CCR4.6: 0 = local loopback disabled */
	/* CCR4.5: 0 = transmit normal data */
	/* CCR4.4: 0 = TCM bit 4 (unused) */
	/* CCR4.3: 0 = TCM bit 3 (unused) */
	/* CCR4.2: 0 = TCM bit 2 (unused) */
	/* CCR4.1: 0 = TCM bit 1 (unused) */
	/* CCR4.0: 0 = TCM bit 0 (unused) */

	xlb[0xaa] = 0x00;	/* CCR5 */
	/* CCR5.7: 0 = no immediate LIRST */
	/* CCR5.6: 0 = no RES align (not assigned DS2154) */
	/* CCR5.5: 0 = no TES align (not assigned DS2154) */
	/* CCR5.4: 0 = RCM bit 4 (unused) */
	/* CCR5.3: 0 = RCM bit 3 (unused) */
	/* CCR5.2: 0 = RCM bit 2 (unused) */
	/* CCR5.1: 0 = RCM bit 1 (unused) */
	/* CCR5.0: 0 = RCM bit 0 (unused) */

	xlb[0x1d] = ccr6;	/* CRC6 */
	/* CCR6.7: 0 = TTIP and TRING normal */
	/* CCR6.6: 0 = normal data */
	/* CCR6.5: 0 = E1 signal */
	/* CCR6.4: 0 = unassigned */
	/* CCR6.3: 0 = unassigned */
	/* CCR6.2: X = Tx clock determined by CRC2.2 (LOTCMC)/Tx clock is RCLK */
	/* CCR6.1: 0 = no RES reset */
	/* CCR6.0: 0 = no TES reset */

	xlb[0xac] = test3;
	/* TEST3: 0x00 = no Rx gain */
	/* TEST3: 0x70 = 30dB Rx gain */
	/* TEST3: 0x72 = 12dB Rx gain */

	xlb[0x18] = licr;
	/* LICR.7: X = LBO bit 2 */
	/* LICR.6: X = LBO bit 1 */
	/* LICR.5: X = LBO bit 0 */
	/* LICR.4: X = -12dB/-43dB Rx EGL */
	/* LICR.3: 0 = JA on receive side */
	/* LICR.2: 0 = JA buffer depth 128 bits */
	/* LICR.1: 0 = JA enabled */
	/* LICR.0: X = transmitters on/off */

	xlb[0x20] = 0x1b;	/* TAFR: Note:The TAF register must be programmed with the 7-bit FAS word
				   (0x1b).  The DS21354/DS21554 do not automatically set these bits. */
	/* TAF.7: 0 = Si bit */
	/* TAF.6: 0 = frame alignment signal bit */
	/* TAF.5: 0 = frame alignment signal bit */
	/* TAF.4: 1 = frame alignment signal bit */
	/* TAF.3: 1 = frame alignment signal bit */
	/* TAF.2: 0 = frame alignment signal bit */
	/* TAF.1: 1 = frame alignment signal bit */
	/* TAF.0: 1 = frame alignment signal bit */

	xlb[0x21] = 0x5f;	/* TNAFR: Note: bit 6 of the TNAF register must be programmed to one.  The
				   DS21354/DS21554 does not automaticallly set this bit. G.704/2.3.2: Bits
				   Sa4 to Sa8 (where these are not used) should be set to 1 on links crossing 
				   an international border.  This also conveys a synchronization status
				   message of "do not use for synchronization" regardless of sychronization
				   status bit assignment. */
	/* TNAF.7 0 = Si bit */
	/* TNAF.6 1 = FNA signal bit */
	/* TNAF.5 0 = remote alarm */
	/* TNAF.4 1 = additional bit 4 */
	/* TNAF.3 1 = additional bit 5 */
	/* TNAF.2 1 = additional bit 6 */
	/* TNAF.1 1 = additional bit 7 */
	/* TNAF.0 1 = additional bit 8 */

	/* CONFIGURE ALL TIMESLOTS. */

	/* set the idle code */
	xlb[0x2a] = 0xff;
	for (offset = 0; offset < 32; offset++) {
		xlb[0x60 + offset] = 0xff;	/* tx idle code */
		xlb[0x80 + offset] = 0xff;	/* rx idle code */
	}
	/* Note that setting up these registers has no effect unless the line framing is set to CAS instead
	   of CCS. */
	xlb[0x40] = 0x0b;	/* TS1: X = 1, Y = 0: CAS alignment word */
	/* TS1.0=1: X: 1 = spare bit */
	/* TS1.1=1: X: 1 = spare bit */
	/* TS1.2=0: Y: 0 = no remote alarm */
	/* TS1.3=1: X: 1 = spare bit */
	/* TS1.4=0: 0: 0 = must be zero */
	/* TS1.5=0: 0: 0 = must be zero */
	/* TS1.6=0: 0: 0 = must be zero */
	/* TS1.7=0: 0: 0 = must be zero */
	for (offset = 0; offset < 4; offset++) {
		/* Note: If CCR3.6 == 1, then a zero in the TCBRs implies that signaling data is to be
		   sourced from TSER (or TSIG if CCR3.2 == 1), and a one implies that signaling data for that 
		   channel is to be sourced from the Transmit Signaling (TS) registers.  In this mode, the
		   voice-channel number scheme (CH1 to CH30) is used. */
		xlb[0x22 + offset] = 0xff;	/* TCB1 to TCB4 = all ones */
	}

	{
		struct ch *ch;
		int chan;

		for (chan = 1; chan < 32; chan++) {
			if ((ch = sp->chans[chan]) != NULL) {
				xp_e1_chan_config(sp, ch, xlb);
				continue;
			}
			swerr();
		}
	}

	/* SET UP INTERRUPT MASK REGISTERS */

	xlb[0x16] = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4);
	/* IMR1.7=0: RSA1: Receive Signaling All Ones/Signaling Change. */
	/* IMR1.6=0: RDMA: Receive Distant MF Alarm. */
	/* IMR1.5=0: RSA0: Receive Signaling All Zeros/Signaling Change. */
	/* IMR1.4=1: RSLIP: Receive Elastic Store Slip Occurrence. */
	/* IMR1.3=1: RUA1: Receive Unframed All Ones. */
	/* IMR1.2=1: RRA: Receive Remote Alarm. */
	/* IMR1.1=1: RCL: Receive Carrier Loss. */
	/* IMR1.0=1: RLOS: Receive Loss of Sync. */

	xlb[0x17] = (1 << 0) | (1 << 2) | (1 << 4);
	/* IMR2.7=0: RMF: Receive CAS Multiframe. */
	/* IMR2.6=0: RAF: Receive Align Frame. */
	/* IMR2.5=0: TMF: Transmit Multiframe. */
	/* IMR2.4=1: SEC: One Second Timer. */
	/* IMR2.3=0: TAF: Transmit Align Frame. */
	/* IMR2.2=1: LOTC: Loss of Transmit Clock. */
	/* IMR2.1=0: RCMF: Receive CRC4 Multiframe. */
	/* IMR2.0=1: TSLIP: Transmit-Side Eleastic Store Slip Occurrence. */

#if 1
	if (timeouts) {
		unsigned long timeout;

		/* line interface reset */
		xlb[0xaa] |= (1 << 7);	/* CCR5 line interface reset (LIRST) */
		timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
		while ((volatile unsigned long)
		       jiffies < timeout) ;
		xlb[0xaa] &= ~(1 << 7);	/* CCR5 */

		/* This must always be done after LIRST. */
		/* reset and realign elastic stores */
		xlb[0x1d] |= ((1 << 1) | (1 << 0));	/* CRC6: reset elastic stores */
		xlb[0x1d] &= ~((1 << 1) | (1 << 0));	/* CRC6 */
		/* CCR6.7: 0 = TTIP and TRING normal */
		/* CCR6.6: 0 = normal data */
		/* CCR6.5: 0 = E1 signal */
		/* CCR6.4: 0 = unassigned */
		/* CCR6.3: 0 = unassigned */
		/* CCR6.2: X = Tx clock determined by CRC2.2 (LOTCMC)/Tx clock is RCLK */
		/* CCR6.1: X = no RES reset */
		/* CCR6.0: X = no TES reset */
		xlb[0xaa] |= ((1 << 6) | (1 << 5));	/* CCR5 realign elastic stores */
		xlb[0xaa] &= ~((1 << 6) | (1 << 5));	/* CCR5 */
	}
#endif
	return (0);
}

noinline __unlikely void
xp_e1_chan_config(struct sp *sp, struct ch *ch, volatile uint8_t *xlb)
{
	if (ch->chan == 0) {
		swerr();
		return;
	}

	/* Per-channel signalling from signalling registers. */
	/* TS2 thru TS16. Set b and d bits transmit signalling each channel.  (ITU-T specifications recommend 
	   that ABCD signalling not be set to all zero because they will emulate a CAS multiframe alignment
	   word. */
	/* Note that this has no effect unless the framing is set to CAS instead of CCS. */
	if (ch->chan < 16)
		/* chan 1 through chan 15 is upper nibble of 0x41 through 0x4f */
		xlb[0x40 + (ch->chan % 16)] = (xlb[0x40 + (ch->chan % 16)] & ~0xf0) | 0x50;
	if (ch->chan > 16)
		/* chan 17 through chan 31 is lower nibble of 0x41 through 0x4f */
		xlb[0x40 + (ch->chan % 16)] = (xlb[0x40 + (ch->chan % 16)] & ~0x0f) | 0x05;

	/* When CAS signalling is used we cannot apply the same logic to channel 16 as we normally do. */
	if (ch->chan == 16) {
		if (sp->config.ifframing == SDL_FRAMING_CAS)
			ch->sdl.config.ifmode = SDL_MODE_SIG;
		else if (ch->sdl.config.ifmode == SDL_MODE_SIG)
			ch->sdl.config.ifmode = SDL_MODE_PEER;
	}

	/* Per-channel quiet code. */
	if (ch->sdl.config.ifmode == SDL_MODE_IDLE) {
		xlb[0xa0 + (sp->span >> 3)] |= (1 << (ch->chan % 8));
		xlb[0xa4 + (sp->span >> 3)] |= (1 << (ch->chan % 8));
	} else {
		xlb[0xa0 + (sp->span >> 3)] &= ~(1 << (ch->chan % 8));
		xlb[0xa4 + (sp->span >> 3)] &= ~(1 << (ch->chan % 8));
	}

	/* Per-channel remote payload loopback. */
	if (ch->sdl.config.ifmode == SDL_MODE_REM_LB)
		xlb[0x26 + (sp->span >> 3)] |= (1 << (ch->chan % 8));
	else
		xlb[0x26 + (sp->span >> 3)] &= ~(1 << (ch->chan % 8));
}

noinline __unlikely void xp_x1_chan_config(struct sp *sp, struct ch *ch, volatile uint8_t *xlb);

noinline __unlikely int
xp_x1_span_config(struct sp *sp, bool timeouts)
{
	volatile unsigned char *xlb = (typeof(xlb)) sp->iobase;
	u_char t1rcr2, t1tcr1, t1tcr2, t1ccr1, e1rcr1, e1tcr1, sigcr, ercnt, ccr1, lic1, lic2, lic3, lic4;
	int err;

	if ((err = xp_allocate_chans(sp)) != 0)
		return (err);

	/* MSTRREG.0: SFTRST: Software issued reset. */
	/* MSTRREG.1: T1/E1: Operating Mode. */
	/* MSTRREG.2: TEST0: 0 = operate normally */
	/* MSTRREG.3: TEST1: 0 = operate normally */
	switch (sp->config.ifgtype) {
	default:
	case SDL_GTYPE_NONE:
		sp->config.ifgtype = SDL_GTYPE_E1;
	case SDL_GTYPE_E1:
		/* MSTRREG.1=1: E1 Operating Mode. */
		xlb[0x00] = (1 << 1);
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		/* MSTRREG.1=0: T1/J1 Operating Mode. */
		xlb[0x00] = (0 << 1);
		break;
	}

	/* IOCR1.7=0 (E1 MR) CAS multiframe boundaries */
	/* IOCR1.6=0 N/A when TSYNC is an input */
	/* IOCR1.5=0 RSYNC pin is frame mode */
	/* IOCR1.4=1 RSYNC is an input (not an output) */
	/* IOCR1.3=0 N/A when TSYNC is an ouput */
	/* IOCR1.2=0 TSYNC pin is frame mode */
	/* IOCR1.1=1 TSYNC is an output (not an input) */
	/* IOCR1.0=0 bibolar (not NRZ) data at TPOSO and TNEGO */

	/* IOCR1.4=1 RSYNC is an input (elastic store). */
	/* IOCR1.1=1 TSYNC is an output, */
	/* IOCR1.0=0 Output data format is bipolar, */
	xlb[0x01] = (1 << 4) | (1 << 1);	/* RSIO + 1 is from O12.0 */

	/* IOCR2.7=0 RCLK no inversion */
	/* IOCR2.6=0 TCLK no inversion */
	/* IOCR2.5=0 RSYNC no inversion */
	/* IOCR2.4=0 TSYNC no inversion */
	/* IOCR2.3=0 TSSYNC no inversion */
	/* IOCR2.2=0 H.100 SYNC mode normal */
	/* IOCR2.1=1 TSYSCLK is 2.048/4.096/8.192MHz or IBO enabled */
	/* IOCR2.0=1 RSYSCLK is 2.048MHz or IBO enabled */
	xlb[0x02] = (1 << 1) | (1 << 0);	/* RSYSCLK/TSYSCLK 8.192MHz IBO */

	/* IBOC.6=0: IBS1: 01 - four devices; 11 - reserved. */
	/* IBOC.5=1: IBS0: 00 - two devices; 10 - Eight devices; */
	/* IBOC.4=0: IBOSEL: IBO Select (0-channel,1-frame) interleave */
	/* IBOC.3=1: IBOEN: Interleave Bus Operation Enable. */
	/* IBOC.2-0=n: DA2-DA0: Position on bus. */
	xlb[0xc5] = (1 << 5) | (1 << 3) | sp->span;

	/* ESCR.7=0 !TESALIGN no align. */
	/* ESCR.6=0 !TESR no reset. */
	/* ESCR.5=0 !TESMDM full two frame depth. */
	/* ESCR.4=1 TESE transmit eleastic store enabled. */
	/* ESCR.3=0 !RESALGN no align. */
	/* ESCR.2=0 !RESR no reset. */
	/* ESCR.1=0 !RESMDM full two frame depth. */
	/* ESCR.0=1 RESE receive eleastic store enabled. */
	xlb[0x4f] = (1 << 4) | (1 << 0);	/* RES/TES (elastic store) enabled */

#if 1
	/* NOTE: this should be done after LIRST */
	/* We should really reset the elastic store after reset like so: */
	/* ESCR.2=1 RESR reset. */
	/* ESCR.6=1 TESR reset. */
	xlb[0x4f] &= ~((1 << 6) | (1 << 2));	/* Clear enabled elastic stores. */
	xlb[0x4f] |= ((1 << 6) | (1 << 2));	/* Reset enabled elastic stores. */
	xlb[0x4f] &= ~((1 << 6) | (1 << 2));	/* Clear enabled elastic stores. */
#endif

#if 1
	/* NOTE: this should be done after LIRST */
	/* And even align it like so: */
	/* ESCR.3=1 RESALGN align. */
	/* ESCR.7=1 TESALIGN align. */
	xlb[0x4f] &= ~((1 << 7) | (1 << 3));	/* Clear enabled elastic stores. */
	xlb[0x4f] |= ((1 << 7) | (1 << 3));	/* Align enabled elastic stores. */
	xlb[0x4f] &= ~((1 << 7) | (1 << 3));	/* Clear enabled elastic stores. */
#endif

	/* CCR1.0=0 RLOS/LOTC ouput is RLOS */
	/* CCR1.3=0 Unused, must set to zero. */
	/* CCR1.4=0 pulses at TPOSO and TNEGO are one full TCLKO period wide */
	/* CCR1.5=0 signalling changes reported on any change. */
	/* CCR1.6=0 transmit CRC-4 generation and insertion normal */
	switch (sp->config.ifclock) {
	default:
		sp->config.ifclock = SDL_CLOCK_LOOP;
	case SDL_CLOCK_LOOP:
		/* Use the signal present at RCLK as the transmit clock.  The TCLK pin is ignored. */
		/* CCR1.1-2 = '11'B Use the signal present at RCLK as the transmit clock. The TCLK pin is
		   ignored. */
		/* CRC1.7=0 MCLK sourced from the MCLK pin. */
		/* default for T1 in tor3 driver, and here */
		ccr1 = (0 << 7) | (0x3 << 1);
		break;
	case SDL_CLOCK_INT:
		/* The TCLK pin is always the source of transmit clock. */
		/* CCR1.1-2 = '00'B The TCLK pin is always the source of transmit clock. */
		/* CRC1.7=0 MCLK sourced from the MCLK pin. */
		ccr1 = (0 << 7) | (0x0 << 1);;
		break;
	case SDL_CLOCK_MASTER:
		/* Use the scaled signal present at MCLK as the transmit clock. The TCLK pin is ignored. */
		/* CCR1.1-2 = '10'B Use the scaled signal present at MCLK as the transmit clock. The TCLK pin 
		   is ignored. */
		/* CRC1.7=0 MCLK sourced from the MCLK pin. */
		ccr1 = (0 << 7) | (0x2 << 1);
		break;
	case SDL_CLOCK_EXT:
		/* Use the scaled signal present at TSYSCLK as the transmit clock.  The TCLK pin is ignored. */
		/* CCR1.1-2 = '10'B Use the scaled signal present at MCLK as the transmit clock. The TCLK pin 
		   is ignored. */
		/* CRC1.7=1 MCLK sourced from the TSYSCLK pin. */
		ccr1 = (1 << 7) | (0x2 << 1);
		break;
	case SDL_CLOCK_SLAVE:
		/* Switch to the clock present at RCLK when the signal at the TCLK pin fails to transition
		   after 1 channel time. */
		/* CCR1.1-2 = '01'B Switch to the clock present at RCLK when the signal at the TCLK pin fails 
		   to transition. */
		/* CRC1.7=0 MCLK sourced from the MCLK pin. */
		/* default for E1 in tor3 driver, and here */
		ccr1 = (0 << 7) | (0x1 << 1);
		break;
	}
	xlb[0x70] = ccr1;

	/* initial signalling register */
	sigcr = 0x00;

	/* initial error counter regsiter */
	ercnt = 0x00;

	/* initial E1 registers */
	e1rcr1 = 0x00;
	e1tcr1 = 0x00;

	/* initial T1 registers */
	t1rcr2 = 0x00;
	t1tcr1 = 0x00;
	t1tcr2 = 0x00;
	t1ccr1 = 0x00;

	switch (sp->config.ifframing) {
	default:
		switch (sp->config.ifgtype) {
		default:
		case SDL_GTYPE_NONE:
		case SDL_GTYPE_E1:
			sp->config.ifframing = SDL_FRAMING_CCS;
			goto framing_ccs;
		case SDL_GTYPE_T1:
		case SDL_GTYPE_J1:
			sp->config.ifframing = SDL_FRAMING_ESF;
			goto framing_esf;
		}
		break;
	case SDL_FRAMING_CCS:
	      framing_ccs:
		sigcr |= (1 << 2);	/* SIGCR.2: RCCS: 1, CCS. */
		sigcr |= (1 << 1);	/* SIGCR.1: TCCS: 1, CCS. */
		e1rcr1 |= (1 << 6);	/* E1RCR1.6: RSIGM: 1, CCS. */
		break;
	case SDL_FRAMING_CAS:
		/* We should really bork on this for all by the MX driver.  Note that when SDL_FRAMING_CAS is 
		   set, channel 16 is unusable on E1. */
		break;
	case SDL_FRAMING_ESF:
	      framing_esf:
		t1rcr2 |= (1 << 6);	/* T1RCR2.6: RFM: 1, Rx ESF. */
		t1ccr1 |= (1 << 2);	/* T1CCR1.2: TFM: 1, Tx ESF. */
		break;
	case SDL_FRAMING_SF:	/* D4 */
		break;
	}
	switch (sp->config.ifcoding) {
	default:
		switch (sp->config.ifgtype) {
		default:
		case SDL_GTYPE_NONE:
		case SDL_GTYPE_E1:
			sp->config.ifcoding = SDL_CODING_HDB3;
			goto coding_hdb3;
		case SDL_GTYPE_T1:
		case SDL_GTYPE_J1:
			sp->config.ifcoding = SDL_CODING_B8ZS;
			goto coding_b8zs;
		}
		break;
	case SDL_CODING_HDB3:
	      coding_hdb3:
		e1rcr1 |= (1 << 5);	/* E1RCR1.5: RHDB3: 1, Rx HDB3. */
		e1tcr1 |= (1 << 2);	/* E1TCR1.2: THDB3: 1, Tx HDB3. */
		ercnt |= (1 << 3);	/* ERCNT.3: VCRFS: 1, count CVs. */
		break;
	case SDL_CODING_AMI:
		break;
	case SDL_CODING_B8ZS:
	      coding_b8zs:
		t1rcr2 |= (1 << 5);	/* T1RCR2.5: RB8ZS: 1, Rx B8ZS */
		t1tcr2 |= (1 << 7);	/* T1TCR2.7: TB8ZS: 1, Tx B8ZS.  */
		break;
	}
	switch (sp->config.ifgcrc) {
	default:
		switch (sp->config.ifgtype) {
		default:
		case SDL_GTYPE_E1:
			sp->config.ifgcrc = SDL_GCRC_CRC4;
			goto gcrc_crc4;
		case SDL_GTYPE_T1:
			sp->config.ifgcrc = SDL_GCRC_CRC6;
			goto gcrc_crc6;
		case SDL_GTYPE_J1:
			sp->config.ifgcrc = SDL_GCRC_CRC6J;
			goto gcrc_crc6j;
		}
		break;
	case SDL_GCRC_CRC5:
		break;
	case SDL_GCRC_CRC4:
	      gcrc_crc4:
		e1rcr1 |= (1 << 3);	/* E1RCR1.3: RCRC4: 1, Rx CRC-4. */
		e1tcr1 |= (1 << 0);	/* E1TCR1.0: TCRC4: 1, Tx CRC-4.  */
		break;
	case SDL_GCRC_CRC6:
	      gcrc_crc6:
		break;
	case SDL_GCRC_CRC6J:
	      gcrc_crc6j:
		t1tcr1 |= (1 << 7);	/* T1TCR1.7: TJC: 1, Tx Japan CRC6J. */
		t1rcr2 |= (1 << 1);	/* T1RCR2.1: RJC: 1, Tx Japan CRC6J. */
		if (sp->config.ifframing == SDL_FRAMING_D4) {
			t1rcr2 |= (1 << 0);	/* T1RCR2.0: RD4YM: 1, Rx Japanese Yellow. */
			t1tcr2 |= (1 << 2);	/* T1TCR2.2: TD4YM: 1, Tx Japanese Yellow. */
		}
		break;
	}

	/* 0x40 is a signalling control register */
	xlb[0x40] = sigcr;	/* RCCS, TCCS (set to zero for T1) */
	/* SIGCR.7: GRSRE: Global Receive Signalling Reinsertion Enabled. 0, no; 1, yes. */
	/* SIGCR.6: -: Unused, must be set to zero. */
	/* SIGCR.5: -: Unused, must be set to zero. */
	/* SIGCR.4: RFE: Receive Freeze Enable. 0, no freezing; 1, allow freezing. */
	/* SIGCR.3: RFF: Receive Force Freeze.  0, no force freeze; 1, force freeze. */
	/* SIGCR.2: RCCS: Receie Time Slot Control for CAS signalling. 0, CAS; 1, CCS. */
	/* SIGCR.1: TCCS: Transmit Time Slot Control for CAS Signalling. 0, CAS; 1, CCS. */
	/* SIGCR.0: FRSAO: Force Receive Signalling All Ones. 0, normal; 1, force to one. */
	xlb[0x41] = ercnt;
	/* ERCNT.7: -: Unused, must be set to zero. */
	/* ERCNT.6: MECU: Manual Error Counter Update.  Toggle to read counters. */
	/* ERCNT.5: ECUS: Error Counter Update Select. 0, second; 1, 42ms(T1)/62ms(E1). */
	/* ERCNT.4: EAMS: Error Accumulation Mode Select. 0, automatic; 1, manual. */
	/* ERCNT.3: VCRFS: Line Code Violation Count Regiser Function Select. 0, BPVs; 1, CVs. */
	/* ERCNT.2: FSBE: PCVCR Fs-Bit Error Report Enable. 0, Ft-bit only; 1, Fs-bit too. */
	/* ERCNT.1: MOSCRF: Multiframe OOS Count Reg Func Sel. 0, F-bit errors; 1, mfs oos.  */
	/* ERCNT.0: LCVCRF: LCV Count Reg Func Sel.  0, no EXZ; 1, count EXZ. */

	if (sp->config.ifgtype == SDL_GTYPE_E1) {
		/* 0x33 to 0x36 are E1 Tx and Rx control registers */
		xlb[0x33] = e1rcr1;	/* RCR4, RHDB3, RSM, SYNCE */
		/* E1RCR1.7: RSERC: RSER Control. 0, rx data; 1, force to 1 under RLOS. */
		/* E1RCR1.6: RSIGM: Receive Signaling Mode Select. 0, CAS; 1, CCS. */
		/* E1RCR1.5: RHDB3: Receive HDB3 Enable. 0, AMI; 1, HDB3. */
		/* E1RCR1.4: RG802: Receive G.802 Enable. 0, disabled; 1, enabled. */
		/* E1RCR1.3: RCRC4: Receve CRC-4 Enable. 0, non-CRC-4; 1, CRC-4. */
		/* E1RCR1.2: FRC: Frame Resync Criteria. 0, 3 FAS error; 1, or bit 2 of non-FAS. */
		/* E1RCR1.1: SYNCE: Sync Enable. 0, auto resync enabled; 1, disabled. */
		/* E1RCR1.0: RESYNC: Resync. Toggle to 1 for resync. */
		xlb[0x34] = (1 << 0);	/* RCL (1ms) */
		/* E1RCR2.7: Sa8S: Sa8-bit select. 0, RLCLK low during bit position; 1, high. */
		/* E1RCR2.6: Sa7S: Sa7-bit select. 0, RLCLK low during bit position; 1, high. */
		/* E1RCR2.5: Sa6S: Sa6-bit select. 0, RLCLK low during bit position; 1, high. */
		/* E1RCR2.4: Sa5S: Sa5-bit select. 0, RLCLK low during bit position; 1, high. */
		/* E1RCR2.3: Sa4S: Sa4-bit select. 0, RLCLK low during bit position; 1, high. */
		/* E1RCR2.2: -: Unused, must be set to zero. */
		/* E1RCR2.1: -: Unused, must be set to zero. */
		/* E1RCR2.0: RCLA: RCL Alternate Criteria. 0, 255 zeros (125us); 1, 2048 zeros (1ms). */
		/* We could be setting automatic report alarm generation (0x01) (T1) or automatic AIS
		   generation (0x02) (E1). */
		xlb[0x35] = e1tcr1 | (1 << 4);	/* TSiS, TCRC4 (from 014.4), THDB3 (from O14.6) */
		/* E1TCR1.7: TFPT: Transmit Time Slot 0 Pass Through. 0, TAF/TNAF; 1, TSER. */
		/* E1TCR1.6: T16S: Transmit Time Slot 16 Data Select. 0, SSIE; 1, TS1-16. */
		/* E1TCR1.5: TUA1: Transmit Unframed All Ones. 0, normal; 1, tua1. */
		/* E1TCR1.4: TSiS: Transmit International Bit Select. 0, TSER; 1, TAF/TNAF. */
		/* E1TCR1.3: TSA1: Transmit Signaling All Ones.  0, normal; 1, all 1's. */
		/* E1TCR1.2: THDB3: Transmit HDB3 Enable.  0, AMI; 1, HDB3. */
		/* E1TCR1.1: TG802: Transmit G.802 Enable. 0, disabled; 1, enabled. */
		/* E1TCR1.0: TCRC4: Transmit CRC-4 Enable. 0, non-CRC-4; 1, CRC-4. */
		xlb[0x36] = (1 << 2) | (1 << 1) | (1 << 0);	/* AEBE=1, AAIS=1, ARAI=1 */
		/* E1TCR2.7: Sa8S: Sa8-bit select. 0 do not source from TLINK pin. */
		/* E1TCR2.6: Sa7S: Sa7-bit select. 0 do not source from TLINK pin. */
		/* E1TCR2.5: Sa6S: Sa6-bit select. 0 do not source from TLINK pin. */
		/* E1TCR2.4: Sa5S: Sa5-bit select. 0 do not source from TLINK pin. */
		/* E1TCR2.3: Sa4S: Sa4-bit select. 0 do not source from TLINK pin. */
		/* E1TCR2.2: AEBE: Automatic E-bit Enable. 1 = E-bits auto set in tx * direction */
		/* E1TCR2.1: AAIS: Automatic AIS generation. 0 = disabled; 1 = enabled. */
		/* E1TCR2.0: ARA: Automatic Remote Alarm Generation. 0 = disabled; 1 = enabled. */
#if 0
		xlb[0x33] = e1rcr1 | (1 << 0);	/* RCR4, RHDB3, RSM, SYNCE, RESYNC */
		xlb[0x33] = e1rcr1 | 0x00;	/* RCR4, RHDB3, RSM, SYNCE */
#endif

		/* 0xd0 and 0xd1 are Tx Sa bits for E1 operation */
		/* This is a little peculiar: the host should be using Method 3 in section 22.3 of the DS2155 
		   manual instead of this method that only permits 250us to read or write the bits. */
		xlb[0xd0] = 0x1b;	/* TAF.0-7='00011011'B (default) */
		/* TAF.7: 0 = Si bit */
		/* TAF.6: 0 = frame alignment signal bit */
		/* TAF.5: 0 = frame alignment signal bit */
		/* TAF.4: 1 = frame alignment signal bit */
		/* TAF.3: 1 = frame alignment signal bit */
		/* TAF.2: 0 = frame alignment signal bit */
		/* TAF.1: 1 = frame alignment signal bit */
		/* TAF.0: 1 = frame alignment signal bit */
		/* Note: bit 6 of the TNAF register must be programmed to one.  The DS21354/DS21554 does not
		   automaticallly set this bit.  G.704/2.3.2: Bits Sa4 to Sa8 (where these are not used)
		   should be set to 1 on links crossing an international border. This also conveys a
		   synchronization status message of "do not use for synchronization" regardless of
		   sychronization status bit assignment. */
		xlb[0xd1] = 0x5f;	/* TNAF.0-7='01011111'B (Sa all ones) XXX */
		/* TNAF.7 0 = Si bit */
		/* TNAF.6 1 = FNA signal bit */
		/* TNAF.5 0 = remote alarm */
		/* TNAF.4 1 = additional bit 4 */
		/* TNAF.3 1 = additional bit 5 */
		/* TNAF.2 1 = additional bit 6 */
		/* TNAF.1 1 = additional bit 7 */
		/* TNAF.0 1 = additional bit 8 */

		/* 0x03 to 0x07 are T1 Tx and Rx control registers */
		xlb[0x03] = 0x00;
		xlb[0x04] = t1rcr2;	/* '00000000'B for E1 */
		xlb[0x05] = t1tcr1;	/* '00000000'B for E1 */
		xlb[0x06] = t1tcr2;	/* '00000000'B for E1 */
		xlb[0x07] = t1ccr1;	/* '00000000'B for E1 */

		/* 0xb6 to 0xbf are programmable in-band loop code generatiion and detection that are only
		   applicable to T1. */
		xlb[0xb6] = 0x00;
		xlb[0xb7] = 0x00;
		xlb[0xb8] = 0x00;
		xlb[0xb9] = 0x00;
		xlb[0xba] = 0x00;
		xlb[0xbb] = 0x00;
		xlb[0xbc] = 0x00;
		xlb[0xbd] = 0x00;
		xlb[0xbe] = 0x00;
		xlb[0xbf] = 0x00;
	} else {
		/* T1TCR1.4=1: TSSE: Tx Soft. Sign. Enable (0, not from regs; 1, from regs) */
		t1tcr1 |= (1 << 4);	/* TSSE */
		{
			int byte, c;
			unsigned short mask = 0;

			/* establish which channels are clear channel for T1 */
			for (c = 0; c < 24; c++) {
				byte = c >> 3;
				if (sp->chans[c + 1]
				    && sp->chans[c + 1]->sdl.config.iftype == SDL_TYPE_DS0A)
					mask |= 1 << (c % 8);
				if ((c % 8) == 7) {
					xlb[0x08 + byte] = mask;
					mask = 0;
				}
			}
		}
		/* 0x03 to 0x07 are T1 Tx and Rx control registers */
#if 0
		xlb[0x03] = 0x08 | 0x00;	/* SYNCC/SYNCE */
		xlb[0x03] = 0x08 | 0x01;	/* SYNCC/SYNCE, RESYNC */
		xlb[0x03] = 0x08 | 0x00;	/* SYNCC/SYNCE */
#else
		xlb[0x03] = 0x00;
#endif
		/* T1RCR1.7 NA: must set to 0 */
		/* T1RCR1.6: ARC: resync criteria (0, OOF or RCL; 1, OOF only) */
		/* T1RCR1.4,5: OOF2,OOF1: (00, 2/4; 10, 2/6; 01, 2/5; 11, 2/6) */
		/* T1RCR1.3: SYNCC: sync criteria (0, simple; 1, complex) */
		/* T1RCR1.2: SYNCT: sync time (0, 10-bits; 1, 24-bits) */
		/* T1RCR1.1: SYNCE: auto resync (0, enabled; 1, disabled) */
		/* T1RCR1.0: RESYNC: toggle 0 to 1 to resync rx framer */

		xlb[0x04] = t1rcr2;	/* RESF RB8ZS RCRC6J */
		/* T1RCR2.7: NA: must be set to 0 */
		/* T1RCR2.6: RFM: framing mode (0, D4; 1, ESF) */
		/* T1RCR2.5: RB8ZS: receive B8ZS (0, disabled; 1, enabled) */
		/* T1RCR2.4: RSLC96: Rx SLC-96 (0, disabled; 1, enabled) */
		/* T1RCR2.3: RZSE: 0 = zero destuffer disabled */
		/* T1RCR2.2: RZBTSI: ZBTSI on RLINK pin (0, disabled; 1, enabled) */
		/* T1RCR2.1: RJC: Japan CRC6 (0, ANSI CRC6; JT-G704 CRC6J ) */
		/* T1RCR2.0: RD4YM: D4 Yellow Alarm (0, 0's in bit 2; 1, 1 in S-bit frame 12) */

		xlb[0x05] = t1tcr1;	/* TSSE TCRC6J */
		/* T1TCR1.7: TJC: Japan CRC6 (0, ANSI CRC6; JT-G704 CRC6J) */
		/* T1TCR1.6: TFPT: F-Bit passthru (0, internal; 1, at TSER) */
		/* T1TCR1.5: TCPT: CRC passthru (0, internal; 1, at TSER) */
		/* T1TCR1.4: TSSE: Tx Soft. Sign. Enable (0, not from regs; 1, from regs) */
		/* T1TCR1.3: GB7S: Global Bit 7 stuffing (0, from regs; 1, from bit 7) */
		/* T1TCR1.2: TFDLS: TFDL select (0, legacy; 1, HDLC or TLINK pin) */
		/* T1TCR1.1: TBL: Transmit blue alarm (0, data; 1, unframed all ones) */
		/* T1TCR1.0 TYEL: Yellow alarm (0, don't tx; 1, do tx) */

		xlb[0x06] = t1tcr2;	/* TB8ZS */
		/* T1TCR2.7: TB8ZS: trans B8ZS (0, disabled; 1, enabled) */
		/* T1TCR2.6: TSLC96: SLC-96 enable (0, disabled; 1, enabled) */
		/* T1TCR2.5: TZSE: FDL zero-stuff (0, HDLC; 1, legacy) */
		/* T1TCR2.4: FBCT2: F-bit corruption 2 (0, disabled; 1, enabled) */
		/* T1TCR2.3: FBCT1: F-bit corruption 1 (0, disabled; 1, enabled) */
		/* T1TCR2.2: TD4YM: D4 Yellow Alarm (0, 0's in bit 2; 1, 1 in S-bit fr 12) */
		/* T1TCR2.1: TZBTSI: ZBTSI support (0, disabled; 1, enabled) */
		/* T1TCR2.0: TB7ZS: Bit 7 stuffing (0, no; 1, stuffing) */

		xlb[0x07] = t1ccr1;	/* TESF */
		/* T1CCR1.7: NA: must set to 0 */
		/* T1CCR1.6: NA: must set to 0 */
		/* T1CCR1.5: NA: must set to 0 */
		/* T1CCR1.4: TRAI-CI: RAI-CI enable (0, no tx; 1, RAI-CI code) ESF */
		/* T1CCR1.3: TAIS-CI: AIS-CI enable (0, no tx; 1, AIS-CI code) TCRC1.1=1 */
		/* T1CCR1.2: TFM: Frame mode (0, D4; 1, ESF) */
		/* T1CCR1.1: PDE: pulse density enforcer (0, disable; 1, enable) */
		/* T1CCR1.0: TLOOP: tx loop-code (0, no; 1, loop per TCD1 and TCD2) */

		/* 0x33 to 0x36 are E1 Tx and Rx control registers */
		xlb[0x33] = 0x00;	/* E1RCR1.0-7='00000000'B */
		xlb[0x34] = 0x00;	/* E1RCR2.0-7='00000000'B */
		xlb[0x35] = 0x00;	/* E1TCR1.0-7='00000000'B */
		xlb[0x36] = 0x00;	/* E1TCR2.0-7='00000000'B */

		/* 0xd0 and 0xd1 are Tx Sa bits for E1 operation */
		xlb[0xd0] = 0x1b;	/* TAF.0-7='00011011'B (default) */
		xlb[0xd1] = 0x40;	/* TNAF.0-7='01000000'B (default) */

		/* 0xb6 to 0xbf are programmable in-band loop code generatiion and detection that are only
		   applicable to T1. */
		xlb[0xb6] = 0x22;	/* IBCC 5-bit loop up, 3-bit loop down code */
		xlb[0xb7] = 0x80;	/* TCD1 - 10000 loop up code (for now) */
		xlb[0xb8] = 0x00;	/* TCD2 don't care */
		xlb[0xb9] = 0x80;	/* RUPCD1 - 10000 receive loop up code */
		xlb[0xba] = 0x00;	/* RUPCD2 don't care */
		xlb[0xbb] = 0x80;	/* RDNCD1 - 100 receive loop down code */
		xlb[0xbc] = 0x00;	/* RDNCD2 don't card */
		xlb[0xbd] = 0x00;
		xlb[0xbe] = 0x00;
		xlb[0xbf] = 0x00;
	}

	if (sp->config.ifgtype == SDL_GTYPE_E1) {
		/* LIC1.4=1 EGL E1 -43dB (long haul) */
		/* LIC1.5-7=001 E1 120 Ohmm normal */
		lic1 = (0x01 << 5) | (1 << 4);
		/* LIC2.3=1 JAMUX Jitter Atten. MUX (0, MCLK 2.048 or 1.544; 1, PLL 2.048 at MCLK */
		/* LIC2.7=1 ETS E1/T1 select (0, T1; 1, E1) */
		lic2 = (1 << 3) | (1 << 7);	/* JACLK on for E1 */
		/* LIC4.0-1=11 RT0-RT1 120 Ohm termination enabled */
		/* LIC4.2-3=11 TT0-TT1 120 Ohm termination enabled */
		/* LIC4.4-5=00 MPS0-MPS1 E1 LIC2.3=0 1.544MHz ??? */
		/* LIC4.6=0 CMI normal */
		/* LIC4.7=0 CMI disabled */
		lic4 = (0x00 << 4) | (0x03 << 2) | (0x03 << 0);	/* 120 Ohm term, MCLK 2.048 MHz */
	} else {
		/* LIC1.4=0 EGL T1 -36db (long haul) */
		/* LIC1.5-7=000 T1 0dB CSU */
		lic1 = (0x00 << 5) | (0 << 4);
		/* LIC2.3=1 JAMUX Jitter Atten. MUX (0, MCLK 2.048 or 1.544; 1, PLL 2.048 at MCLK */
		/* LIC2.7=0 ETS E1/T1 select (0, T1; 1, E1) */
		lic2 = (1 << 3);	/* JACLK on for T1 */
		/* LIC4.0-1=10 RT0-RT1 100 Ohm termination enabled */
		/* LIC4.2-3=10 TT0-TT1 100 Ohm termination enabled */
		/* LIC4.4-5=00 MPS0-MPS1 T1 LIC2.3=0 1.544MHz, LIC2.3=1 2.048MHz */
		/* LIC4.6=0 CMI normal */
		/* LIC4.7=0 CMI disabled */
		lic4 = (0x00 << 4) | (0x02 << 2) | (0x02 << 0);	/* 100 Ohm term, MCLK 2.048 MHz */
	}
	lic3 = 0x00;		/* no gain */
	if (sp->config.iftxlevel < 8) {
		lic1 |= (1 << 0);	/* LIC1.0=1 TBD transmitter on */
		if (sp->config.ifgtype != SDL_GTYPE_E1)
			lic1 |= ((sp->config.iftxlevel & 0x7) << 5);	/* LBO */
	} else {
		/* monitoring mode */
		lic1 &= ~(1 << 0);	/* LIC1.0=0 TBD transmitter off */
		lic3 |= ((sp->config.iftxlevel & 0x3) << 3);	/* Linear gain */
		/* 00 - 0dB linear gain */
		/* 01 - 20dB linear gain (single 432/470 Ohm tap) */
		/* 10 - 26dB linear gain (double 432/470 Ohm tap) */
		/* 11 - 30dB linear gain (triple 432/470 Ohm tap) */
	}

	xlb[0x78] = lic1 & ~(1 << 0);	/* don't power up transmitter yet */
	/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
	/* LIC1.1=0 DJA Disable Jitter Attenuator (0, enabled; 1, disabled) */
	/* LIC1.2=0 JABDS JA Buffer Depth (0, 128bits; 1, 32bits) */
	/* LIC1.3=0 JAS JA select (0, on Rx side; 1, on Tx side) */
	/* LIC1.4=? EGL Receive Equal. Gain Limit */
	/* T1: 0, -36dB (long haul); 1, -15dB (short haul) */
	/* E1: 0, -12dB (short haul); 1, -43dB (long haul) */
	/* LIC1.5-7=??? LBO */

	xlb[0x79] = lic2;
	/* LIC2.0 CLDS Custom Line Driver Select (0, normal) */
	/* LIC2.1 SCLD Short Circuit Limit Disable (0, enable; 1, disable) */
	/* LIC2.2 Unused, zero for proper operation */
	/* LIC2.3 JAMUX Jitter Atten. MUX (0, MCLK 2.048 or 1.544; 1, PLL 2.048 at MCLK */
	/* LIC2.4 TUA1 Transmit Unframed All Ones (0, all ones; 1, normal data) */
	/* LIC2.5 IBPF Insert BPV (0=>1 insert BPV) */
	/* LIC2.6 LISRT Line Interface Reset (0=>1, reset) */
	/* LIC2.7 ETS E1/T1 select (0, T1; 1, E1) */

	xlb[0x7a] = lic3;
	/* LIC3.7: -: Unused. must be set to zero. */
	/* LIC3.6: TCES: TCLKI Edge Select. 0 = rising; 1 = falling. */
	/* LIC3.5: RCES: RCLKO Edge Select. 0 = rising; 1 = falling. */
	/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
	/* LIC3.2: RSCLKE: Tx Sync G.703 Clock Span. 0 = disable; 1 = enable. */
	/* LIC3.1: TSCLKE: Rx Sync G.703 Clock Span. 0 = disable; 1 = enable. */
	/* LIC3.0: TAOZ: Tx Alternal Ones and Zeros (CDI). 0, disable; 1, enable. */

	xlb[0x7b] = lic4;
	/* LIC4.0-1 RT0-RT1 Internal Receive Termination. */
	/* 00 - internal termination disabled */
	/* 01 - 75 Ohm termination enabled */
	/* 10 - 100 Ohm termination enabled */
	/* 11 - 120 Ohm termination enabled */
	/* LIC4.2-3 TT0-TT1 Internal Transmit Termination. */
	/* 00 - internal termination disabled */
	/* 01 - 75 Ohm termination enabled */
	/* 10 - 100 Ohm termination enabled */
	/* 11 - 120 Ohm termination enabled */
	/* LIC4.4-5 MPS0-1 MBLK Prescaler */
	/* T1 00 LIC2.3=0 1.544 MHz */
	/* T1 01 LIC2.3=0 3.088 MHz */
	/* T1 10 LIC2.3=0 6.176 MHz */
	/* T1 11 LIC2.3=0 12.352 MHz */
	/* T1 00 LIC2.3=1 2.048 MHz */
	/* T1 01 LIC2.3=1 4.096 MHz */
	/* T1 10 LIC2.3=1 8.192 MHz */
	/* T1 11 LIC2.3=1 16.384 MHz */
	/* E1 00 LIC2.3=0 1.544 MHz */
	/* E1 01 LIC2.3=0 3.088 MHz */
	/* E1 10 LIC2.3=0 6.176 MHz */
	/* E1 11 LIC2.3=0 12.352 MHz */
	/* LIC4.6 CMII CMI Invert (0, normal; 1, invert) */
	/* LIC4.7 CMIE CMI Enable (0, disable; 1, enable) */

	xlb[0x7d] = 0x00;	/* Automatic gain control */
	xlb[0x78] = lic1 | (1 << 0);	/* power up transmitter if not monitoring */

	/* CONFIGURE ALL TIMESLOTS. */

	xlb[0x7e] = (1 << 7) | (1 << 6);	/* IAAR.6 (GRIC), IAAR.7 (GTIC) */

	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		xlb[0x7f] = 0xff;	/* PCICR, set idle code to 0xff. */
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		xlb[0x7f] = 0x7f;	/* PCICR, set idle code to 0x7f. */
		break;
	}

	{
		struct ch *ch;
		int chan;

		if (sp->config.ifgtype == SDL_GTYPE_E1) {
			/* Note that setting CAS registers has no effect unless the line framing is set to
			   CAS instead of CSS. */
			xlb[0x50] = (1 << 3) | (1 << 1) | (1 << 0);	/* TS1: X = 1, Y = 0: CAS alignment
									   word. */
			/* TS1.0=1: X: 1 = spare bit */
			/* TS1.1=1: X: 1 = spare bit */
			/* TS1.2=0: Y: 0 = no remote alarm */
			/* TS1.3=1: X: 1 = spare bit */
			/* TS1.4=0: 0: 0 = must be zero */
			/* TS1.5=0: 0: 0 = must be zero */
			/* TS1.6=0: 0: 0 = must be zero */
			/* TS1.7=0: 0: 0 = must be zero */

			for (chan = 1; chan < 32; chan++) {
				if ((ch = sp->chans[chan]) != NULL) {
					xp_x1_chan_config(sp, ch, xlb);
					continue;
				}
				swerr();
			}
		} else {
			for (chan = 1; chan < 24; chan++) {
				if ((ch = sp->chans[chan]) != NULL) {
					xp_x1_chan_config(sp, ch, xlb);
					continue;
				}
				swerr();
			}
		}
	}

	/* SET UP INTERRUPT MASK REGISTERS */

	xlb[0x17] = (1 << 7) | (1 << 6) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0);
	/* IMR1.7=1: ILUT: Input Level Under Threshold. */
	/* IMR1.6=1: TIMER: Timer Event. */
	/* IMR1.5=0: RSCOS: Receive Signaling Change-of-State Event. */
	/* IMR1.4=1: JALT: Jitter Attenuator Limit Trip Event (JALT). */
	/* IMR1.3=1: LRCL: Line Interface Receive Carrier Loss Condition. */
	/* IMR1.2=1: TCLE: Transmit Current Limit Exceeded. */
	/* IMR1.1=1: TOCD: Transmit Open Circuit Detect Condition. */
	/* IMR1.0=1: LOLITC: Loss of Transmit Clock Condition. */

	xlb[0x19] = (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
	/* IMR2.7=0: RYELC: Receive Yellow Alarm Clear Event. */
	/* IMR2.6=1: RUA1C: Receive Unframed All Ones Condition Clear Event. */
	/* IMR2.5=1: FRCLC: Receive Carrier Loss Condition Clear. */
	/* IMR2.4=1: RLOSC: Receive Loss of Sync Clear Event. */
	/* IMR2.3=0: RYEL: Receive Yellow Alarm Condition. */
	/* IMR2.2=1: RUA1: Receive Unframed All Ones (Blue Alarm) Condition. */
	/* IMR2.1=1: FRCL: Framer Receive Carrier Loss Condition. */
	/* IMR2.0=1: RLOS: Receive Loss of Sync Condition. */
	if (sp->config.ifgtype != SDL_GTYPE_E1)
		/* IMR2.7=1: RYELC: Receive Yellow Alarm Clear Event. */
		/* IMR2.3=1: RYEL: Receive Yellow Alarm Condition. */
		xlb[0x19] |= (1 << 7) | (1 << 3);

	xlb[0x1b] = (1 << 3) | (1 << 4);	/* 'XXX11XXX' */
	/* IMR3.7=0: LSPARE: Spare Code Detected Condition. */
	/* IMR3.6=0: LDN: Loop-Down Code Detected Condition. */
	/* IMR3.5=0: LUP: Loop-Up Code Detected Condition. */
	/* IMR3.4=1: LOTC: Loss of Transmit Clock Condition. */
	/* IMR3.3=1: LORC: Loss of Receive Clock Condition. */
	/* IMR3.2=0: V52LNK: V5.2 Link Detected Condition. */
	/* IMR3.1=0: RDMA: Receive Distant MF Alarm Condition. */
	/* IMR3.0=0: RRA: Receive Remote Alarm Condition. */
	if (sp->config.ifgtype == SDL_GTYPE_E1)
		/* IMR3.2=1: V52LNK: V5.2 Link Detected Condition. */
		/* IMR3.1=1: RDMA: Receive Distant MF Alarm Condition. */
		/* IMR3.0=1: RRA: Receive Remote Alarm Condition. */
		xlb[0x1b] |= (1 << 2) | (1 << 1) | (1 << 0);	/* 'XXXXX111' */
	else
		/* IMR3.7=1: LSPARE: Spare Code Detected Condition. */
		/* IMR3.6=1: LDN: Loop-Down Code Detected Condition. */
		/* IMR3.5=1: LUP: Loop-Up Code Detected Condition. */
		xlb[0x1b] |= (1 << 7) | (1 << 6) | (1 << 5);	/* '111XXXXX' */

	xlb[0x1d] = (0 << 4) | (0 << 2);	/* 'tee0e0ee' */
	/* IMR4.7=0: RAIS-CI: Receive AIS-CI Event. */
	/* IMR4.6=0: RSA1: Receive Signalling All-Ones Event. */
	/* IMR4.5=0: RSA0: Receive Signalling All-Zeros Event. */
	/* IMR4.4=0: TMF: Transmit Multiframe Event. */
	/* IMR4.3=0: TAF: Transmit Align Frame. */
	/* IMR4.2=0: RMF: Receive Multiframe Event. */
	/* IMR4.1=0: RCMF: Receive CRC-4 Multiframe Event. */
	/* IMR4.0=0: RAF: Receive Align Frame. */
	if (sp->config.ifgtype == SDL_GTYPE_E1)
		/* IMR4.6=1: RSA1: Receive Signalling All-Ones Event. */
		/* IMR4.5=1: RSA0: Receive Signalling All-Zeros Event. */
		xlb[0x1d] |= (1 << 6) | (1 << 5);
	else
		/* IMR4.7=1: RAIS-CI: Receive AIS-CI Event. */
		xlb[0x1d] |= (1 << 7);

	xlb[0x1f] = (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0);
	/* IRM5.7=0: -: */
	/* IMR5.6=0: -: */
	/* IMR5.5=1: TESF: Transmit Elastic Store Full Event. */
	/* IMR5.4=1: TESEM: Transmit Elastic Store Empty Event. */
	/* IMR5.3=1: TSLIP: Transmit Elastic Store Slip Occurrence Event. */
	/* IMR5.2=1: RESF: Receive Elastic Store Full Event. */
	/* IMR5.1=1: RESEM: Receive Elastic Store Empty Event. */
	/* IMR5.0=1: RSPLIP: Receive Elastic Store Slip Occurrence Event. */

	xlb[0x21] = 0x00;	/* IMR6 */
	xlb[0x23] = 0x00;	/* IMR7 */
	xlb[0x25] = 0x00;	/* IMR8 */
	xlb[0x27] = 0x00;	/* IMR9 */

#if 0
	if (timeouts) {
		unsigned long timeout;

		xlb[0x79] = lic2 | 0x40;	/* LIRST on */
		timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
		while ((volatile unsigned long)
		       jiffies < timeout) ;
		xlb[0x79] = lic2;	/* LIRST off */
	}
#endif
	return (0);
}

noinline __unlikely void
xp_x1_chan_config(struct sp *sp, struct ch *ch, volatile uint8_t *xlb)
{
	if (ch->chan == 0) {
		swerr();
		return;
	}
	switch (ch->sdl.config.ifgtype) {
	case SDL_GTYPE_E1:
		if (ch->chan < 16) {
			int m = 1, c = ch->chan - m;

			/* chan 1 through chan 15 is lower then upper nibble of 0x51(TS2) through 0x58(TS9)
			   ending on lower nibble */
			if (c & 0x01) {
				/* upper-nibble */
				xlb[0x50 + m + (c >> 1)] = (xlb[0x50 + m + (c >> 1)] & 0xf0) | 0x50;
			} else {
				/* lower-nibble */
				xlb[0x50 + m + (c >> 1)] = (xlb[0x50 + m + (c >> 1)] & 0x0f) | 0x05;
			}
		}
		if (ch->chan > 16) {
			int m = 0, c = ch->chan - m;

			/* chan 17 through chan 31 is upper, lower then upper nibble of 0x58(TS9) through
			   0x5f(TS16). */
			if (c & 0x01) {
				/* upper-nibble */
				xlb[0x50 + m + (c >> 1)] = (xlb[0x50 + m + (c >> 1)] & 0xf0) | 0x50;
			} else {
				/* lower-nibble */
				xlb[0x50 + m + (c >> 1)] = (xlb[0x50 + m + (c >> 1)] & 0x0f) | 0x05;
			}
		}
		if (ch->chan != 16) {
			if (sp->config.ifframing == SDL_FRAMING_CAS)
				xlb[0x08 + (ch->chan >> 3)] |= (1 << (ch->chan % 8));
			else
				xlb[0x08 + (ch->chan >> 3)] &= ~(1 << (ch->chan % 8));
		} else {
			/* When CAS signalling is used we cannot apply the same logic to channel 16 as we
			   normally do. */
			if (sp->config.ifframing == SDL_FRAMING_CAS)
				ch->sdl.config.ifmode = SDL_MODE_SIG;
			else if (ch->sdl.config.ifmode == SDL_MODE_SIG)
				ch->sdl.config.ifmode = SDL_MODE_PEER;
		}
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		/* Note that setting the TS1 through TS12 registers has no effect unless the channel is
		   marked for robbed-bit signalling. */
		if (ch->chan <= 24) {
			int c = ch->chan - 1;

			/* chan 1 through chan 24 is TS1 through TS12, lower then upper nibble */
			if (c & 0x01) {
				/* upper nibble */
				xlb[0x50 + (c >> 1)] = (xlb[0x50 + (c >> 1)] & 0xf0) | 0x50;
			} else {
				/* lower nibble */
				xlb[0x50 + (c >> 1)] = (xlb[0x50 + (c >> 1)] & 0x0f) | 0x05;
			}
		}
		if (ch->sdl.config.iftype == SDL_TYPE_DS0A)
			/* DS0A does not need to be set for clear-channel operation. */
			xlb[0x08 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
		else
			/* DS0 needs to be set for clear-channel operation. */
			xlb[0x08 + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
		break;
	default:
		swerr();
		return;
	}

	/* Per-channel quiet code. */
	if (ch->sdl.config.ifmode == SDL_MODE_IDLE) {
		xlb[0x80 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
		xlb[0x84 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
	} else {
		xlb[0x80 + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
		xlb[0x84 + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
	}

	/* Per-channel milliwatt. */
	if (ch->sdl.config.ifgtype != SDL_GTYPE_E1) {
		if (ch->sdl.config.ifmode == SDL_MODE_MW) {
			xlb[0x80 + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
			xlb[0x0c + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
		} else
			xlb[0x0c + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
	}

	/* Per-channel remote payload loopback. */
	if (ch->sdl.config.ifmode == SDL_MODE_REM_LB)
		xlb[0x4b + ((ch->chan - 1) >> 3)] |= (1 << ((ch->chan - 1) % 8));
	else
		xlb[0x4b + ((ch->chan - 1) >> 3)] &= ~(1 << ((ch->chan - 1) % 8));
}

noinline __unlikely int xp_t1_card_config(struct cd *cd);
noinline __unlikely int xp_e1_card_config(struct cd *cd);
noinline __unlikely int xp_x1_card_config(struct cd *cd);

/**
  * xp_card_config: - perform initial configure of a card
  * @cd: card structure
  */
noinline __unlikely int
xp_card_config(struct cd *cd)
{
	int err;

	switch (cd->device) {
	case XP_DEV_DS21352:
	case XP_DEV_DS21552:
		err = xp_t1_card_config(cd);
		break;
	case XP_DEV_DS21354:
	case XP_DEV_DS21554:
		err = xp_e1_card_config(cd);
		break;
	case XP_DEV_DS2155:
	case XP_DEV_DS21455:
	case XP_DEV_DS21458:
		err = xp_x1_card_config(cd);
		break;
	default:
		swerr();
		err = -EFAULT;
	}

	return (err);
}

/** xp_t1_card_config: - configure a card with a T1 device
  * @cd: card structure pointer
  */
noinline __unlikely int
xp_t1_card_config(struct cd *cd)
{
	int span, offset;
	unsigned long timeout;

	cd->xlb[CTLREG] = cd->ctlreg = cd->clkreg = 0;	/* 1.544 MHz Crystal */
	cd->xlb[SYNREG] = cd->synreg = SYNCSELF;	/* Sync to crystal */
	cd->xlb[LEDREG] = cd->ledreg = 0;
	cd->xlb[TSTREG] = cd->tstreg = 0;	/* do not drive TEST2 pin */

	if (cd->devrev > 3) {
		cd->xlb[CTLREG1] = NONREVA;	/* non Rev. A mode */
		cmn_err(CE_NOTE, "%s: setting non-Rev.A mode", DRV_NAME);
	} else {
		cd->xlb[CTLREG1] = 0;	/* Rev. A mode */
		cmn_err(CE_NOTE, "%s: setting Rev.A mode", DRV_NAME);
	}
	if (japan) {
		/* setup J1 card defaults */
		cd->config = sdl_default_j1_chan;
	} else {
		/* setup T1 card defaults */
		cd->config = sdl_default_t1_chan;
	}

	/* Idle out all channels.  The idle byte depends on whether the span is configured for E1 operation
	   or T1 or J1 operation.  This sets the registers as well as setting the driver's elastic buffers. */
	{
		int word;
		uint idle_word = 0x7f7f7f7f;

		/* idle out all channels */
		for (word = 0; word < 256; word++) {
			int ebuf;

			cd->xll[word] = idle_word;
			for (ebuf = 0; ebuf < X400P_EBUFNO; ebuf++)
				cd->wbuf[(ebuf << 8) + word] = idle_word;
		}
	}

	/* On power-up, after supplies are stable the DS21352/552 should be configured for operation by
	   writing all of the internal registers (this includes setting test registers to 00h) since the
	   contents of the internal registers cannot be predicted on power-up. The LIRST (CCR7.7) should be
	   toggled from zero to one to reset the line interface circuitry (it will take the DS21352/552 about 
	   40ms to recover from the LIRST bit being toggled).  Finally, after the TSYSCLK and RSYSCLK inputs
	   are stable, the ESR bit should be toggled from zero to a one (this step can be skipped if the
	   elastic stores are disabLed). */

	/* Zero all span registers, note that the DS21352/552 only have 160 defined registers. */
	for (span = 0; span < X400_SPANS; span++)
		for (offset = 0; offset < 160; offset++)
			cd->xlb[(span << 8) + offset] = 0x00;

	/* The interleaved PCM bus option (IBO) supports two bus speeds. The 4.096 MHz bus speed allows two
	   SCTs to share a common bus.  The 8.192 MHz bus speed allows 4 SCTs to share a common bus.  Each
	   SCT that shares a common bus must be configured through software and requires the use of one or
	   two device pins. The elastic stores of each SCT must be enabled and configured for 2.048 MHz
	   operation. For all bus configuration, one SCTP will be configured as the master device and the
	   remaining SCTs will be configured as slave devices. */

	/* IBO.0: MSEL1: '00' slave; '10' master, 1 slave. */
	/* IBO.1: MSEL0: '01' master 3 slaves; '11' reserved. */
	/* IBO.2: INTSEL: Interleave type (0, byte; 1, frame) */
	/* IBO.3: IBOEN: Interleave enable (0, disabled; 1, enabled) */
	/* IBO.4-7: NA: should be set to zero. */
	/* set up for interleaved serial bus operation, byte mode */
	cd->xlb[0x094] = 0x09;	/* IBOEN, '01' master, 3 slaves */
	cd->xlb[0x194] = 0x08;	/* IBOEN, '00' slave */
	cd->xlb[0x294] = 0x08;	/* IBOEN, '00' slave */
	cd->xlb[0x394] = 0x08;	/* IBOEN, '00' slave */

	cd->config.ifflags = (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING);

	/* Allocate span structures and write all of the registers of interest. */
	for (span = 0; span < cd->ports; span++) {
		struct sp *sp;
		int err;

		if (!(sp = cd->spans[span])) {
			if (!(sp = xp_alloc_sp(cd, span)))
				return (-ENOMEM);
		} else
			xp_init_sp(cd, sp, span);

		if ((err = xp_t1_span_config(sp, 0)) != 0)
			return (err);
	}

	/* CCR7.7: LIRST: Line Interface Reset */
	/* CCR7.6: RLB: Remote loopback (0, disabled; 1, enabled) */
	/* CCR7.5: RESR: Receive elastic store reset. */
	/* CCR7.4: TESR: Transmit elastic store reset. */
	/* CCR7.3: NA: set to zero */
	/* CCR7.2: LIUSI: LI sync interface (0, normal; 1, sync signal only) */
	/* CCR7.1: CDIG: Customer disconnect indication (0, normal; 1, unframed ..1010.. */
	/* CCR7.0: LIUODO: LI open drain (0, normal; 1, TTIP/TRING open drain) */
	/* line interface reset */
	/* CCR7.7=1 (LIRST) */
	cd->xlb[0x00a] |= (1 << 7);
	cd->xlb[0x10a] |= (1 << 7);
	cd->xlb[0x20a] |= (1 << 7);
	cd->xlb[0x30a] |= (1 << 7);

	/* Wait 40 milliseconds. */
	timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
	while ((volatile unsigned long) jiffies < timeout) ;

	/* release LIRST bit */
	/* CCR7.7=0 (~LIRST) */
	cd->xlb[0x00a] &= ~(1 << 7);
	cd->xlb[0x10a] &= ~(1 << 7);
	cd->xlb[0x20a] &= ~(1 << 7);
	cd->xlb[0x30a] &= ~(1 << 7);

	/* At this point we are ready to detect the line connection.  This determination is more difficult on 
	   the DS21[35]52 because there is no transmitter open-circuit detection. */

	/* reset elastic stores */
	/* CCR7.4=1 (TESR), CCR7.5=1 (RESR) */
	cd->xlb[0x00a] |= ((1 << 4) | (1 << 5));
	cd->xlb[0x10a] |= ((1 << 4) | (1 << 5));
	cd->xlb[0x20a] |= ((1 << 4) | (1 << 5));
	cd->xlb[0x30a] |= ((1 << 4) | (1 << 5));
	/* CCR7.4=0 (~TESR), CCR7.5=0 (~RESR) */
	cd->xlb[0x00a] &= ~((1 << 4) | (1 << 5));
	cd->xlb[0x10a] &= ~((1 << 4) | (1 << 5));
	cd->xlb[0x20a] &= ~((1 << 4) | (1 << 5));
	cd->xlb[0x30a] &= ~((1 << 4) | (1 << 5));

	/* align elastic stores */
	/* CCR6.5=1 (TESA), CCR6.6=1 (RESA) */
	cd->xlb[0x01e] |= ((1 << 5) | (1 << 6));
	cd->xlb[0x11e] |= ((1 << 5) | (1 << 6));
	cd->xlb[0x21e] |= ((1 << 5) | (1 << 6));
	cd->xlb[0x31e] |= ((1 << 5) | (1 << 6));
	/* CCR6.5=0 (~TESA), CCR6.6=0 (~RESA) */
	cd->xlb[0x01e] &= ~((1 << 5) | (1 << 6));
	cd->xlb[0x11e] &= ~((1 << 5) | (1 << 6));
	cd->xlb[0x21e] &= ~((1 << 5) | (1 << 6));
	cd->xlb[0x31e] &= ~((1 << 5) | (1 << 6));

	return (0);
}

/** xp_e1_card_config: - configure a card with an E1 device
  * @cd: card structure pointer
  */
noinline __unlikely int
xp_e1_card_config(struct cd *cd)
{
	int span, offset;
	unsigned long timeout;

	/* 2.048 MHz Crytal on-board. */
	cd->xlb[CTLREG] = cd->ctlreg = cd->clkreg = E1DIV;
	cd->xlb[SYNREG] = cd->synreg = SYNCSELF;	/* Sync to crystal. */
	cd->xlb[LEDREG] = cd->ledreg = 0;
	cd->xlb[TSTREG] = cd->tstreg = 0;	/* don't drive TEST2 pin */

	if (cd->devrev > 3) {
		cd->xlb[CTLREG1] = NONREVA;	/* non Rev. A mode */
		cmn_err(CE_NOTE, "%s: setting non-Rev.A mode", DRV_NAME);
	} else {
		cd->xlb[CTLREG1] = 0;	/* Rev. A mode */
		cmn_err(CE_NOTE, "%s: setting Rev.A mode", DRV_NAME);
	}

	/* setup E1 card defaults */
	cd->config = sdl_default_e1_chan;

	/* Idle out all channels.  The idle byte depends on whether the span is configured for E1 operation
	   or T1 or J1 operation.  This sets the registers as well as setting the driver's elastic buffers. */
	{
		int word;
		uint idle_word = 0xffffffff;

		/* idle out all channels */
		for (word = 0; word < 256; word++) {
			int ebuf;

			cd->xll[word] = idle_word;
			for (ebuf = 0; ebuf < X400P_EBUFNO; ebuf++)
				cd->wbuf[(ebuf << 8) + word] = idle_word;
		}
	}

	/* On power-up, after the supplies are stable, the DS21345/DS21554 should be configured for operation 
	   by writing to all the internal registers (this includes setting test registers to 00h) since the
	   contents of the internal registers cannot be predicted on power-up. The LIRST (CCR5.7) should be
	   toggled from zero to one to reset the line interface circuitry (it will take the device about 40ms 
	   to recover from the LIRST bit being toggled).  Finally, after TSYSCLK and RSYSCLK inputs are
	   stable, the ESR bits (CCR6.0 and CCR6.1) should be toggled from a zero to a one (this step can be
	   skipped if the elastic stores are disabled). */

	/* Zero all span registers.  Note that these devices have a full 256 register locations. */
	for (span = 0; span < X400_SPANS; span++)
		for (offset = 0; offset < 256; offset++)
			cd->xlb[(span << 8) + offset] = 0x00;

	/* The interleaved PCM bus option (IBO) supports two bus speeds.  The 4.096 MHz bus speed allows to
	   share a common bus.  The 8.192 MHz bus speed allows four SCTs to share a common bus.  Each SCT
	   that shares a common bus must be configured through software and requires the use of one or two
	   device pints. The elastic stores of each SCT must be enabled and configured for 2.048 MHz
	   operation.  For all bus configurations, one SCT will be configured as the master device and the
	   remaining SCTs will be configured as slave devices.  In the 8.192 MHz bus configuration there is
	   one master and three slaves. */

	/* set up for interleaved serial bus operation, byte mode */
	/* IBO.7: NA: should be set to 0 */
	/* IBO.6: NA: should be set to 0 */
	/* IBO.5: NA: should be set to 0 */
	/* IBO.4: NA: should be set to 0 */
	/* IBO.3: IBOEN: IBO enable (0, disabled; 1, enabled) */
	/* IBO.2: INTSEL: Interleave type (0, byte; 1, frame) */
	/* IBO.1: MSEL0: '00' slave; '10' master with 1 slave */
	/* IBO.0: MSEL1: '01' master with 3 slaves; '11' reserved */
	cd->xlb[0x0b5] = 0x09;	/* IBOEN, master w/ 3 slaves, 8.192 MHz bus */
	cd->xlb[0x1b5] = 0x08;	/* IBOEN, slave */
	cd->xlb[0x2b5] = 0x08;	/* IBOEN, slave */
	cd->xlb[0x3b5] = 0x08;	/* IBOEN, slave */

	cd->config.ifflags = (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING);

	/* Allocate span structures and write all of the registers of interest. */
	for (span = 0; span < cd->ports; span++) {
		struct sp *sp;
		int err;

		if (!(sp = cd->spans[span])) {
			if (!(sp = xp_alloc_sp(cd, span)))
				return (-ENOMEM);
		} else
			xp_init_sp(cd, sp, span);

		if ((err = xp_e1_span_config(sp, 0)) != 0)
			return (err);
	}

	/* line interface reset */
	/* CCR5.7=1 (LIRST) */
	cd->xlb[0x0aa] |= (1 << 7);
	cd->xlb[0x1aa] |= (1 << 7);
	cd->xlb[0x2aa] |= (1 << 7);
	cd->xlb[0x3aa] |= (1 << 7);

	/* Wait 40 milliseconds. */
	timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
	while ((volatile unsigned long) jiffies < timeout) ;

	/* release LIRST bit */
	/* CCR5.7=0 (~LIRST) */
	cd->xlb[0x0aa] &= ~(1 << 7);
	cd->xlb[0x1aa] &= ~(1 << 7);
	cd->xlb[0x2aa] &= ~(1 << 7);
	cd->xlb[0x3aa] &= ~(1 << 7);

	/* At this point we are ready to detect the line connection.  We initially configure sending a blue
	   alarm (AIS) unframed all ones, with the transmitters enabled. This is because these chips cannot
	   detect open circuit. What we are trying to figure out is whether the receivers and/or transmitters 
	   are connected.  If we are live on a functional span, the receivers will be reporting no alarm
	   (other than AIS), and the receivers will be synced.  The receivers will be reporting AIS or remote 
	   AIS. Receivers: LORC: loss of receive carrier: RX not connected or monitoring. LOS: loss of sync:
	   Rx connected, perhaps wrong framing or wrong E1/T1 setting. RAIS: Rx connected, Tx connected. RAI: 
	   Tx not connected, or alarm settling. */

	/* reset elastic store */
	/* CCR6.0=1 (RESR), CCR6.1=1 (TESR) */
	cd->xlb[0x01d] |= ((1 << 0) | (1 << 1));
	cd->xlb[0x11d] |= ((1 << 0) | (1 << 1));
	cd->xlb[0x21d] |= ((1 << 0) | (1 << 1));
	cd->xlb[0x31d] |= ((1 << 0) | (1 << 1));
	/* CCR6.0=0 (~RESR), CCR6.1=0 (~TESR) */
	cd->xlb[0x01d] &= ~((1 << 0) | (1 << 1));
	cd->xlb[0x11d] &= ~((1 << 0) | (1 << 1));
	cd->xlb[0x21d] &= ~((1 << 0) | (1 << 1));
	cd->xlb[0x31d] &= ~((1 << 0) | (1 << 1));

	/* align elastic store */
	/* CCR5.5=1 (RESA), CCR5.6=1 (TESA) */
	cd->xlb[0x0aa] |= ((1 << 5) | (1 << 6));
	cd->xlb[0x1aa] |= ((1 << 5) | (1 << 6));
	cd->xlb[0x2aa] |= ((1 << 5) | (1 << 6));
	cd->xlb[0x3aa] |= ((1 << 5) | (1 << 6));
	/* CCR5.5=0 (~RESA), CCR5.6=0 (~TESA) */
	cd->xlb[0x0aa] &= ~((1 << 5) | (1 << 6));
	cd->xlb[0x1aa] &= ~((1 << 5) | (1 << 6));
	cd->xlb[0x2aa] &= ~((1 << 5) | (1 << 6));
	cd->xlb[0x3aa] &= ~((1 << 5) | (1 << 6));

	return (0);
}

/** xp_x1_card_config: - configure a card with an E1/T1 device
  * @cd: card structure pointer
  */
noinline __unlikely int
xp_x1_card_config(struct cd *cd)
{
	int span;
	unsigned long timeout;
	int err;

	switch (cd->board) {
	case V400PE:
	case A400PE:
	case AE400P:
		/* 2.048 MHz Crystal on board.  */
		cd->xlb[CTLREG] = cd->ctlreg = cd->clkreg = E1DIV;
		break;
	case V400PT:
	case A400PT:
	case AT400P:
		/* 1.544 MHz Crystal on board.  */
		cd->xlb[CTLREG] = cd->ctlreg = cd->clkreg = 0;
		break;
	case A400P:
	case V400P:
		/* Should only get here for A400P as V400P has DS21Q352 or DS21Q354 chip instead of E1/T1
		   chip. */
		/* 2.048 MHz Crystal on board.  */
		cd->xlb[CTLREG] = cd->ctlreg = cd->clkreg = E1DIV;
		break;
	case CP100: case CP100P: case CP100E:
	case CP200: case CP200P: case CP200E:
	case CP400: case CP400P: case CP400E:
		/* 2.048 MHz Crystal on board.  */
		cd->xlb[CTLREG] = cd->ctlreg = cd->clkreg = E1DIV;
		break;
	}
	cd->xlb[SYNREG] = cd->synreg = SYNCSELF;	/* Sync to crystal for now. */
	cd->xlb[LEDREG] = cd->ledreg = 0;
	cd->xlb[TSTREG] = cd->tstreg = 0;	/* do not drive TEST2 pin */

	if (cd->devrev > 3) {
		cd->xlb[CTLREG1] = NONREVA;	/* non Rev. A mode */
		cmn_err(CE_NOTE, "%s: setting non-Rev.A mode", DRV_NAME);
	} else {
		cd->xlb[CTLREG1] = 0;	/* Rev. A mode */
		cmn_err(CE_NOTE, "%s: setting Rev.A mode", DRV_NAME);
	}
	if (japan) {
		/* setup J1 card defaults */
		cd->config = sdl_default_j1_chan;
	} else if (etsi) {
		/* setup E1 card defaults */
		cd->config = sdl_default_e1_chan;
	} else if (ansi) {
		/* setup T1 card defaults */
		cd->config = sdl_default_t1_chan;
	} else {
		switch (cd->board) {
		case V401PE:
		case A400PE:
			/* setup E1 card defaults */
			cd->config = sdl_default_e1_chan;
			break;
		case V401PT:
		case A400PT:
			/* setup T1 card defaults */
			cd->config = sdl_default_t1_chan;
			break;
		case V400P:
		case A400P:
		case CP100: case CP100P: case CP100E:
		case CP200: case CP200P: case CP200E:
		case CP400: case CP400P: case CP400E:
		default:
			/* setup T1/E1/J1 card defaults */
			cd->config = sdl_default_t1_chan;
			break;
		}
	}

	/* Allocate span structures and idle out all channels.  The idle byte depends on whether the span is
	   configured for E1 operation or T1 or J1 operation.  This sets the registers as well as setting the 
	   driver's elastic buffers. */
	{
		int word, idle_word;

		switch (__builtin_expect(cd->config.ifgtype, SDL_GTYPE_E1)) {
		default:
		case SDL_GTYPE_E1:
			idle_word = 0xffffffff;
			break;
		case SDL_GTYPE_T1:
		case SDL_GTYPE_J1:
			idle_word = 0x7f7f7f7f;
			break;
		}

		/* idle out all channels */
		for (word = 0; word < 256; word++) {
			int ebuf;

			cd->xll[word] = idle_word;
			for (ebuf = 0; ebuf < X400P_EBUFNO; ebuf++)
				cd->wbuf[(ebuf << 8) + word] = idle_word;
		}
	}

	/* The DS2155/DS21455/DS21458 contains an on-chip power-up reset function that automatically clears
	   the writeable register space immediately after power is supplied to the device. The user can issue 
	   a chip reset at any time.  Issuing a reset disrupts traffic flowing until the device is
	   reprogrammed. The reset can be issued through hardware using the TSTRST pin or through software
	   using the SFTRST function in the master mode register.  The LIRST (LIC2.6) should be toggled from
	   zero to one to reset the line interface circuitry. (It takes the DS2155 about 40ms to recover from 
	   the LIRST bit being toggled.) Finally, after the TSYSCLK and RSYSCLK inputs are stable, the
	   receive and transmit elastic stores should be reset (this step can be skipped if the elastic
	   stores are disabled.) */

	/* No need to zero all registers.  Note that TAF and TNAF do not initialize to zero anyway and would
	   be disrupted by writing zeros everywhere. */

	/* Interleaved PCM Bus Operation: The interleaved PCM bus option (IBO) supports three bus speeds. The 
	   4.096 MHz bus speed allows two PCM data streams to share a common bus.  The 8.192 MHZ bus speed
	   allows four PCM data streams to share a common bus.  The 16.384 MHz bus speed allows eight PCM
	   data streams to share a common bus.  THE RECEIVE ELASTIC STORES OF EACH TRANSCEIVER MUST BE
	   ENABLED. Via the IBO register, the user can configure each transceiver for a specific bus
	   position.  For all IBO bus configurations each transceiver is assigned an exclusive position in
	   the high-speed PCM bus. The 8kHz frame sync can be generated from the system backplane or from the 
	   first device on the bus. ALL OTHER DEVICES ON THE BUS MUST HAVE THEIR FRAME SYNCS CONFIGURED AS
	   INPUTS. Relative to this common frame sync, the devices will await their turn to drive or sample
	   the bus according to the setting of the DA0, DA1, and DA2 bits of the IBOC register. In channel
	   interleave mode, data is output to the PCM data-out bus one channel at a time from each of the
	   connected devices until all channels of frame n from each device has been placed on the bus. This
	   mode can be used even when the DS21455/DS21458s are operating asyncrhonous to each other.  The
	   elastic stores will manage slip conditions. In frame interleave mode, data is output to the PCM
	   data-out bus one frame at a time from each of the devices. This mode is used when all connected
	   devices are operating in syncrhonous fashion (all inbound T1 or E1 lines are syncrhonous) and are
	   synchronous with the system clock (system clock derived from T1 or E1 line). In this mode slip
	   conditions are not allowed. */

	/* set up for interleaved serial bus operation, byte mode */
	/* IBOC.7: -: Unused.  set to zero. */
	/* IBOC.6: IBS1: 01 - four devices; 11 - reserved. */
	/* IBOC.5: IBS0: 00 - two devices; 10 - Eight devices; */
	/* IBOC.4: IBOSEL: IBO Select (0-channel,1-frame) interleave */
	/* IBOC.3: IBOEN: Interleave Bus Operation Enable. */
	/* IBOC.2-0: DA2-DA0: Position on bus. */
	cd->xlb[0x0c5] = 0x28 + 0;	/* IBO, channel, 4 devices, 1st device. */
	cd->xlb[0x1c5] = 0x28 + 1;	/* IBO, channel, 4 devices, 2nd device. */
	cd->xlb[0x2c5] = 0x28 + 2;	/* IBO, channel, 4 devices, 3rd device. */
	cd->xlb[0x3c5] = 0x28 + 3;	/* IBO, channel, 4 devices, 4th device. */

	cd->config.ifflags = (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING);

	/* Allocate span structures and Write all registers of interest.  The reason that we do not do LIRST
	   in the span configuration is because we want to start them all and then wait 40ms once for all
	   instead of waiting 40ms once for each (which would be 160ms). */
	for (span = 0; span < cd->ports; span++) {
		struct sp *sp;
		int err;

		if (!(sp = cd->spans[span])) {
			if (!(sp = xp_alloc_sp(cd, span)))
				return (-ENOMEM);
		} else
			xp_init_sp(cd, sp, span);

		if ((err = xp_x1_span_config(sp, 0)) != 0)
			return (err);
	}

	/* LIC2.3: JAMUX-PLL: by setting this bit, we take the jitter attenuator input from the Phase Locked
	   Loop instead of from MCLK.  This allows running a T1 or J1 Span when the E1 divider for the board
	   is set to E1DIV. This might be adjusted later on a span by span basis.  We can run T1 or J1 spans
	   on a card set for E1, but not visa versa.  This is why we set the V400P and A400P to E1 mode. */
	switch (cd->board) {
	}

	/* line interface reset */
	/* LIC2.6=1 (LIRST) */
	cd->xlb[0x079] |= (1 << 6);
	cd->xlb[0x179] |= (1 << 6);
	cd->xlb[0x279] |= (1 << 6);
	cd->xlb[0x379] |= (1 << 6);

	/* Wait 40 milliseconds. */
	timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
	while ((volatile unsigned long) jiffies < timeout) ;

	/* release LIRST bit */
	/* LIC2.6=0 (~LIRST) */
	cd->xlb[0x079] &= ~(1 << 6);
	cd->xlb[0x179] &= ~(1 << 6);
	cd->xlb[0x279] &= ~(1 << 6);
	cd->xlb[0x379] &= ~(1 << 6);

	/* reset elastic stores */
	/* ESCR.2=1 (RESR), ESCR.6=1 (TESR) */
	cd->xlb[0x04f] |= ((1 << 2) | (1 << 6));
	cd->xlb[0x14f] |= ((1 << 2) | (1 << 6));
	cd->xlb[0x24f] |= ((1 << 2) | (1 << 6));
	cd->xlb[0x34f] |= ((1 << 2) | (1 << 6));
	/* ESCR.2=0 (~RESR), ESCR.6=0 (~TESR) */
	cd->xlb[0x04f] &= ~((1 << 2) | (1 << 6));
	cd->xlb[0x14f] &= ~((1 << 2) | (1 << 6));
	cd->xlb[0x24f] &= ~((1 << 2) | (1 << 6));
	cd->xlb[0x34f] &= ~((1 << 2) | (1 << 6));

	/* align elastic stores */
	/* ESCR.3=1 (RESALGN), ESCR.7=1 (TESALGN) */
	cd->xlb[0x04f] |= ((1 << 2) | (1 << 6));
	cd->xlb[0x14f] |= ((1 << 2) | (1 << 6));
	cd->xlb[0x24f] |= ((1 << 2) | (1 << 6));
	cd->xlb[0x34f] |= ((1 << 2) | (1 << 6));
	/* ESCR.3=0 (~RESALGN), ESCR.7=0 (~TESALGN) */
	cd->xlb[0x04f] &= ~((1 << 2) | (1 << 6));
	cd->xlb[0x14f] &= ~((1 << 2) | (1 << 6));
	cd->xlb[0x24f] &= ~((1 << 2) | (1 << 6));
	cd->xlb[0x34f] &= ~((1 << 2) | (1 << 6));

	/* align elastic store */

	/* At this point we are ready to detect the line connection for each span.  This determination is
	   easier on the DS2155 series because they have transmitter open-circuit detection. When the
	   transmitters are open-circuited, there are several possibilities: either the span is disconnected
	   altogether, or it is connected to a low impedance monitoring jack, or it is connected to a high
	   impedance monitoring jack. When the receiver detects a carrier, it could be connected to a low
	   impedance monitoring jack. If the receiver is also recovering clock and is synchronized, then the
	   framing is correct and we can just mark monitoring mode. When the receive does not detect a
	   carrier, we can try applying a linear gain to the receiver and see if we can detect a carrier.
	   Experience shows that +30-32dB is required to get the receivers working on a high-impedance port.
	   We will probably have to wait another number of milliseconds to get things to work. When the
	   receiver does not detect a carrier, even with linear gain applied, then the span is completely
	   disconnected. When the transmitters are not open-circuited, the receivers should be detecting a
	   carrier. Otherwise, the span is faulty and should be disabled.  When detecting a carrier, the
	   receiver should be recovering clock and may or may not be synced. When the receiver is synced and
	   reporting AIS or RAIS, everything is correct and we can mark the span up.  Otherwise, the E1/T1/J1
	   setting or framing might be incorrect.  When we are set for T1 on a J1 link, we should be getting
	   errors associated with CRC6/CRC6J mismatches. In that case, we can set J1 and try again. When we
	   are set for T1 ESF and we are not receiving RAIS, it is likely because D4 framing is being used, so 
	   we can set D4 and perhaps SLC-96. When we are set for E1 the CAS/CCS CRC4/CRC5 HDB3/AMI settings
	   might be off.  When we are set for T1 the DS0/DS0a CRC6/CRC6J ESF/D4 B8ZS/AMI settings might be
	   off. We can use the real-time INFO registers for checking all of this stuff.  When we change
	   something, it might require waiting an additional step. */

	/* INFO1.0: FBE: Frame bit error event.  Set when a Ft (D4) or FPS (ESF) framing bit is received in
	   error. This might indicate that the ESF/D4 setting is incorrect. */
	/* INFO1.1: B8ZS: B8ZS Codeword detect event.  Set when a B8ZS codeword is detected a RPOS and RNEG
	   independent of whether the B8ZS mode is selected or not via T1TRC2.7.  This can indicate that B8ZS 
	   setting is correct, or misset to AMI. */
	/* INFO1.2: SEFE: Severely errored framing event.  Set when two out of six framing bits (Ft or FPS)
	   are received in error.  This can indicate that the ESF/D4 setting is incorrect. */
	/* INFO1.3: 16ZD: Sixteen zero detect event.  Set when a string of at least 16 consecutive zeros
	   (regardless of the length of the string) has been received at RPOSI or RNEGI. */
	/* INFO1.4: 8ZD: Eight zero detect event.  Set when a string of eight consecutive zeros (regardless
	   of the length of the string) have been received at RPOSI and RNEGI. */
	/* INFO1.5: COFA: Change of frame alignment event.  Set when the last resync resulted in a change of
	   frame or multiframe alignment. */
	/* INFO1.6: TPDV: Transmit pulse density violation.  Set when the transmit data stream does not meet
	   the ANSI T1.403 requirements for pulse density. */
	/* INFO1.7: RPDV: Receive pulse density violation.  Set when the receive data does not meet the ANSI
	   T1.403 requiremenst for pulse density. This can mean that the line coding is AMI instead of B8ZS. */
	/* INFO2.0-3: RL0-RL3: Receive level.  Indicates the receive level in -2.5dB increments down less
	   than -37.5dB. Note that the DS2155 only indicates the signal range as specified by the EGL bit in
	   LIC1. */
	/* INFO2.4: TOCD: Tranmit open-circuit detect.  A real-time bit set when the device detects that the
	   TTIP and TRING outputs are open-circuited. */
	/* INFO2.5: TCLE: Transmit current limit exceeded.  A real-time bit set when the 50mA (RMS) current
	   limiter is activated, whether the curren limiter is enabled or not.  This can indicate a
	   short-circuit condition on the transmitter. */
	/* INFO2.6: BD: BOC detected.  A real-time bit that is set high when the BOC detector is presently
	   seeing a valid sequence and set low when no BOC is currently being detected. */
	/* INFO2.7: BERT: BERT real-time synchronization status. */
	/* INFO3.0: CASRC: CAS resync criteria met event.  Set when two consecutive CAS MF alignment words
	   are received in error.  Note: During a CRC resync, the FAS syncrhonizer is brought online to
	   verify FAS alignment.  If during this process a FAS emulator exists, the FAS synchronizer may
	   temporarily align to the emulator.  The FASRC will go active indicating a search for a valid FAS
	   has been activated. */
	/* INFO3.1: FASRC: FAS resync criteria met.  Set when three consecutive FAS words are received in
	   error. */
	/* INFO3.2: CRCRC: CRC resync criteria met.  Set when 915/1000 codewords are received in error. */
	/* INFO7.0: CRC4SA: CRC-4 MF sync active.  Set when the syncrhonizer is searching for the CRC-4 MF
	   alignment word. */
	/* INFO7.1: CASSA: CAS MF sync active.  Set while the syncrhonizer is searching for the CAS MF
	   alignment word. */
	/* INFO7.2: FASSA: FAS sync active.  Set while the syncrhonizer is searching for alignment at the FAS 
	   level. */
	/* INFO7.3-7: CSC0,CSC2-CSC4: CRC-4 Sync counter bits. */

	/* SR1.0: LOLITC: Loss of line interface transmit clock condition.  Set when TCLKI has not
	   transitioned for one channel time. */
	/* SR1.1: TOCD: Transmit open-circuit detect condition.  Set when the device detects that the TTIP
	   and TRING output are open-circuited. */
	/* SR1.2: TCLE: Transmit current limit exceeded.  Set when the 50mA (RMS) current limiter is
	   activated, whether the current limiter is enabled or not. */
	/* SR1.3: LRCL: Line interface receive carrier-loss condition.  Set when the carrier signal is lost. */
	/* SR1.4: JALT: Jitter attenuator limit trip event.  Set when the jitter attenuator FIFO reaches to
	   within 4 bits of its useful limit.  This bit is cleared when read. */
	/* SR1.5: RSCOS: Receive signalling change of state event.  Set when any channel selected by the
	   receive signalling change-of-state interrupt-enable registers (RSCSE1 through RSCSE4) change
	   signalling state. */
	/* SR1.6: TIMER: Timer event. */
	/* SR1.7: ILUT: Input level under threshold.  This bit is set whenever the input level at RTIP and
	   RRING falls below the threshold set by the valud in CCR4.4 through CCR4.7.  The level must remain
	   below the programmed threshold for approximately 50ms for this bit to be set. */

	/* SR2.0: RLOS: Receive loss-of-sync condition.  Set when the DS2155 is not syncrhonized to the
	   received data stream. */
	/* SR2.1: FRCL: Framer receive carrier-loss condition.  Set when 255 (or 2048 if E1RCRC2.0 = 1) E1
	   mode or 192 T1 mode consecutive 0s have been detected at RPOSI and RNEGI. */
	/* SR2.2: RUA1: Receive unframed all-ones condition.  Set when an unframed all 1s code is received at 
	   RPOS1 and RNEG1. */
	/* SR2.3: RYEL: Receive yellow alarm condition.  Set when a yellow alarm is received at RPOSI and
	   RNEGI. */
	/* SR2.4: RLOSC: Receive loss-of-sync clear event.  Set when the carrier loss condition at RPOSI and
	   RNEGI is no longer detected. */
	/* SR2.5: FRCLC: Framer receive carrier-lost clear event.  Set when the carrier loss condition at
	   RPOSI and RNEGI is no longer detected. */
	/* SR2.6: RUA1C: Receive unframed all-ones clear event.  Set when the unframed all ones condition is
	   no longer detected. */
	/* SR2.7: RYELC: Receive yellow alarm clear event.  Set when the receive Yellow Alarm condition is no 
	   longer detected. */

	/* SR3.0: RRA: Receive remote alarm condition.  Set when a remote alarm is received at RPOSI and
	   RNEGI. */
	/* SR3.1: RDMA: Receive distant MF alarm condition.  Set when bit 6 of time slot 16 in frame 0 has
	   been set for two consecutive multiframes. This alarm is not disabled in the CCS signaling mode. */
	/* SR3.2: V52LINK:V5.2 link detected condition.  Set on detection of a V5.2 link identification
	   signal (G.965). */
	/* SR3.3: LORC: Loss of receive clock condition.  Set when the RCLKI pin has not transitioned for one 
	   channel time. */
	/* SR3.4: LOTC: Loss of transmit clock condition.  Set when the TCLK pin has not transitioned for one 
	   channel time. */
	/* SR3.5: LUP: Loop-up code detected condition. Set when the loop-up code as defined in the RUPCD1/2
	   register is being received. */
	/* SR3.6: LDN: Loop-down code detected condition.  Set when the loop-down code as defined in the
	   RDNCD1/2 register is being received. */
	/* SR3.7: LSPARE: Spare code detected condition.  Set when the spare code as defined in the RSCD1/2
	   registers is being received. */

	/* SR4.0: RAF: Receive align frame event.  Set every 250us at the beginning of align frames.  Used to 
	   alert the host that Si and Sa bits are available in the RAF and RNAF registers. */
	/* SR4.1: RCMF: Receive CRC multiframe event.  Set on CRC4 multiframe boundaries; continues to set
	   every 2ms on an arbitrary boundary if CRC4 is disabled. */
	/* SR4.2: RMF: Receive multiframe event.  E1: Set every 2ms (regardless if CAS signaling is enabled
	   or not) on receive multiframe boundaries. Used to alert the host that signaling data is available. 
	   T1: Set every 1.5ms on D4 MF boundaries or every 3ms on ESF MF boundaries. */
	/* SR4.3: TAF: Transmit align frame event.  Set every 250us at the beginning of align frames.  Used
	   to alert the host that the TAF and TNAF registers need to be updated. */
	/* SR4.4: TMF: Transmit multiframe event.  E1: Set every 2ms (regardless if CRC4 is enabled) on
	   transmit multiframe boundaries.  Used to alert the host that signalling data needs to be updated. */
	/* SR4.5: RSAZ: Receive signalling all-zeros event.  Set when over a full MF, time slot 16 contains
	   all zeros. */
	/* SR4.6: RSAO: Receive signalling all-ones event.  Set when the contents of time slot 16 contains
	   few than three 0s over 16 consecutive frames. This alarm is not disabled in the CCS signaling
	   mode. */
	/* SR4.7: RAIS-CI:Set when the receiver detects the AIS-CI pattern as defined in ANSI T1.403. */

	for (span = 0; span < cd->ports; span++) {
		struct sp *sp;
		volatile uint8_t *xlb = &cd->xlb[span << 8];
		uint8_t status, info;
		int tod = 0, tcle = 0, lrcl = 0, jalt = 0, ilut = 0;
		int retry = 0;

		if ((sp = cd->spans[span]) == NULL)
			continue;

	      retry:
		/* SR1.1: TOCD: Transmit open-circuit detect condition.  Set when the device detects that the 
		   TTIP and TRING output are open-circuited. */
		/* SR1.2: TCLE: Transmit current limit exceeded.  Set when the 50mA (RMS) current limiter is
		   activated, whether the current limiter is enabled or not. */
		/* SR1.3: LRCL: Line interface receive carrier-loss condition. Set when the carrier signal is 
		   lost. */
		/* SR1.4: JALT: Jitter attenuator limit trip event.  Set when the jitter attenuator FIFO
		   reaches to within 4 bits of its useful limit. This bit is cleared when read. */
		/* SR1.7: ILUT: Input level under threshold.  This bit is set whenever the input level at
		   RTIP and RRING falls below the threshold set by the valud in CCR4.4 through CCR4.7.  The
		   level must remain below the programmed threshold for approximately 50ms for this bit to be 
		   set. */
		xlb[0x16] = 0x9e;
		status = xlb[0x16] & 0x0e;

		/* check transmitter open-circuit */
		if (status & 0x02) {
			/* transmitters were open-circuited, read real-time */
			xlb[0x11] = 0x10;
			info = xlb[0x11] & 0x10;
			if (info) {
				/* transmitters currently open-circuited */
				tod = 1;
			}
		}
		/* check transmitter short-circuit */
		if (status & 0x04) {
			/* transmitter current limit was exceeded, read real-time */
			xlb[0x11] = 0x20;
			info = xlb[0x11] & 0x20;
			if (info) {
				/* tranmitter current limit currently exceeded */
				tcle = 1;
			}
		}
		/* check line inteface receive carrier loss */
		if (status & 0x08) {
			lrcl = 1;
		}
		/* check the jitter attenuator trip limit */
		if (status & 0x10) {
			jalt = 1;
		}
		/* check input level under threshold */
		if (status & 0x80) {
			ilut = 1;
		}
		if (tcle) {
			/* The transmitter is short circuited.  Shut down the transmitter to protect it and
			   mark the transmitter down. */
			/* Welllll, we might want to leave the transmitter up because it cannot detect short
			   circuit recovery when it is tri-stated. */
			xlb[0x78] &= ~0x01;	/* shut down transmitter */
			sp->config.ifflags &= ~SDL_IF_TX_RUNNING;
		}
		if (tod) {
			/* The transmitter is open-circuited.  Shut down the transmitter and mark it down. */
			/* Welllll, we might want to leave the transmitter up because it cannot detect open
			   circuit recovery when it is tri-stated. */
			xlb[0x78] &= ~0x01;	/* shut down transmitter */
			sp->config.ifflags &= ~SDL_IF_TX_RUNNING;
		}
		if (!tod && !tcle) {
			/* The transmitter is connected and operating normally.  Mark the transmitter up. */
			sp->config.ifflags |= SDL_IF_TX_RUNNING;
		}
		if (lrcl) {
			/* The receivers are disconnected or there is a gain problem. */
		} else {
			/* The receivers are connected and receiving carrier, mark the receivers up. */
			sp->config.ifflags |= SDL_IF_RX_RUNNING;

			if (jalt) {
				/* A jitter attenuator trip limit being exceeded this fast might indicate
				   that the line rate is rather different than what we set. */
				switch (sp->config.ifgtype) {
				default:
				case SDL_GTYPE_NONE:
				case SDL_GTYPE_E1:
					/* We have E1 set, if the user did not explicitly request etsi when
					   the driver was loaded, try switching to T1 and LIRST again. */
					if (!etsi && !retry) {
						sp->config = sdl_default_t1_chan;
						if ((err = xp_x1_span_config(sp, 1)) != 0)
							return (err);
						retry = 1;
						goto retry;
					}
					break;
				case SDL_GTYPE_T1:
					/* We have T1 set, if the user did not explicitly request ansi when
					   the driver was loaded, try switching it to E1 and LIRST again. */
					if (!ansi && !retry) {
						sp->config = sdl_default_e1_chan;
						if ((err = xp_x1_span_config(sp, 1)) != 0)
							return (err);
						retry = 1;
						goto retry;
					}
					break;
				case SDL_GTYPE_J1:
					/* We have J1 set, if the user did not explicitly request japan when
					   the driver was loaded, try switching it to E1 and LIRST again. */
					if (!japan && !retry) {
						sp->config = sdl_default_e1_chan;
						if ((err = xp_x1_span_config(sp, 1)) != 0)
							return (err);
						retry = 1;
						goto retry;
					}
					break;
				}
			} else {
				/* The jitter attenuator is within limits meaning that the rate is likely set 
				   correctly.  Now check some things about whether the settings are also
				   correct. */
				switch (sp->config.ifgtype) {
				default:
				case SDL_GTYPE_E1:
					break;
				case SDL_GTYPE_T1:
					break;
				case SDL_GTYPE_J1:
					break;
				}
			}
		}
	}
	return (0);
}

/**
 * xp_span_reconfig: perform reconfiguration of a running span
 * @cd: card structure owning span
 * @span: span number
 */
noinline __unlikely int
xp_span_reconfig(struct sp *sp)
{
	int err;

	switch (sp->cd->device) {
	case XP_DEV_DS21352:
	case XP_DEV_DS21552:
		err = xp_t1_span_config(sp, 1);
		break;
	case XP_DEV_DS21354:
	case XP_DEV_DS21554:
		err = xp_e1_span_config(sp, 1);
		break;
	case XP_DEV_DS2155:
	case XP_DEV_DS21455:
	case XP_DEV_DS21458:
		err = xp_x1_span_config(sp, 1);
		break;
	default:
		swerr();
		err = -EFAULT;
		break;
	}
	return (err);
}

noinline __unlikely void xp_t1_card_reconfig(struct cd *cd);
noinline __unlikely void xp_e1_card_reconfig(struct cd *cd);
noinline __unlikely void xp_x1_card_reconfig(struct cd *cd);

/**
 * xp_card_reconfig: - perform reconfiguration of a running card
 * @cd: card structure
 */
noinline __unlikely void
xp_card_reconfig(struct cd *cd, int restart)
{
	uint8_t ctlreg = 0, synreg;
	uint8_t span;

	/* do not disrupt loopback bits */
	ctlreg &= ~LSERLB;
	if (cd->config.ifgmode & SDL_GMODE_LOC_LB)
		ctlreg |= LSERLB;
	ctlreg &= ~RSERLB;
	if (cd->config.ifgmode & SDL_GMODE_REM_LB)
		ctlreg |= RSERLB;
	/* do not disrupt master bit */
	ctlreg &= ~MASTER;
	if (cd->config.ifclock == SDL_CLOCK_MASTER)
		ctlreg |= MASTER;
	/* calculate the e1div bit */
	ctlreg &= ~E1DIV;
	switch ((synreg = cd->config.ifsync)) {
	case SYNC1:
	case SYNC2:
	case SYNC3:
	case SYNC4:
		if (cd->spans[synreg - 1]->config.ifgtype == SDL_GTYPE_E1)
			ctlreg |= E1DIV;
		break;
	case SYNCEXTERN:
		if (ctlreg & MASTER) {
			swerr();
			ctlreg &= ~MASTER;
		}
		if (cd->sg.sg != NULL && cd->sg.sg->master != NULL) {
			if (cd->sg.sg->ifgtype == SDL_GTYPE_E1)
				ctlreg |= E1DIV;
			break;
		}
	default:
	case SYNCSELF:
		ctlreg |= cd->clkreg;
		break;
	}
	cd->plx[INTCSR] = 0;	/* disable interrupts */

	if (cd->synreg != synreg)
		cd->eval_syncsrc = 1;	/* reevaluate syncrhonization source */

	cd->xlb[CTLREG] = cd->ctlreg = ctlreg;	/* disable interrupts default mode */
	cd->xlb[SYNREG] = cd->synreg = synreg;
	// cd->xlb[LEDREG] = cd->ledreg = 0; /* turn all led off */ /* leaved leds alone
	// */
	// cd->xlb[TSTREG] = cd->tstreg = 0; /* don't drive TEST2 */

	if (restart) {
		/* Easiest to distinguish functions be device rather than by card. */
		switch (cd->device) {
		case XP_DEV_DS21354:
		case XP_DEV_DS21554:
			xp_e1_card_reconfig(cd);
			break;
		case XP_DEV_DS21352:
		case XP_DEV_DS21552:
			xp_t1_card_reconfig(cd);
			break;
		case XP_DEV_DS2155:
		case XP_DEV_DS21455:
		case XP_DEV_DS21458:
			xp_x1_card_reconfig(cd);
			break;
		default:
			swerr();
			break;
		}

		/* reconfigure all spans */
		for (span = 0; span < cd->ports; span++) {
			struct sp *sp;
			int chan;

			if ((sp = cd->spans[span]) == NULL)
				continue;

			/* Any full span remains a full span of the new type, but DS0a changes to DS0 on E1. */
			switch (cd->config.ifgtype) {
			case SDL_GTYPE_E1:
				switch (sp->config.iftype) {
				case SDL_TYPE_E1:
					break;
				case SDL_TYPE_T1:
				case SDL_TYPE_J1:
					sp->config.iftype = sdl_default_e1_span.iftype;
					sp->config.ifrate = sdl_default_e1_span.ifrate;
					sp->config.ifblksize = sdl_default_e1_span.ifblksize;
					break;
				case SDL_TYPE_DS0A:
					sp->config.iftype = sdl_default_e1_chan.iftype;
					sp->config.ifrate = sdl_default_e1_chan.ifrate;
				case SDL_TYPE_DS0:
					sp->config.ifblksize = sdl_default_e1_chan.ifblksize;
					break;
				}
				sp->config.ifmode = sdl_default_e1_span.ifmode;
				sp->config.ifgtype = sdl_default_e1_span.ifgtype;
				sp->config.ifgrate = sdl_default_e1_span.ifgrate;
				sp->config.ifgmode = sdl_default_e1_span.ifgmode;
				sp->config.ifgcrc = sdl_default_e1_span.ifgcrc;
				sp->config.ifclock = sp->config.ifclock;
				sp->config.ifcoding = sdl_default_e1_span.ifcoding;
				sp->config.ifframing = sdl_default_e1_span.ifframing;
				/* reconfigure all chans */
				for (chan = 0; chan < 32; chan++) {
					struct ch *ch;

					if ((ch = sp->chans[chan]) == NULL)
						continue;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_T1:
					case SDL_TYPE_J1:
						ch->sdl.config.iftype = sdl_default_e1_span.iftype;
						ch->sdl.config.ifrate = sdl_default_e1_span.ifrate;
						ch->sdl.config.ifblksize = sdl_default_e1_span.ifblksize;
						break;
					case SDL_TYPE_DS0A:
						ch->sdl.config.iftype = sdl_default_e1_chan.iftype;
						ch->sdl.config.ifrate = sdl_default_e1_chan.ifrate;
					case SDL_TYPE_DS0:
						ch->sdl.config.ifblksize = sdl_default_e1_chan.ifblksize;
						break;
					}
					ch->sdl.config.ifmode = sdl_default_e1_chan.ifmode;
				}
				break;
			case SDL_GTYPE_T1:
				switch (sp->config.iftype) {
				case SDL_TYPE_E1:
				case SDL_TYPE_J1:
					sp->config.iftype = sdl_default_t1_span.iftype;
					sp->config.ifrate = sdl_default_t1_span.ifrate;
					sp->config.ifblksize = sdl_default_t1_span.ifblksize;
					break;
				case SDL_TYPE_DS0:
				case SDL_TYPE_DS0A:
					sp->config.ifblksize = sdl_default_t1_chan.ifblksize;
					break;
				}
				sp->config.ifmode = sdl_default_t1_span.ifmode;
				sp->config.ifgtype = sdl_default_t1_span.ifgtype;
				sp->config.ifgrate = sdl_default_t1_span.ifgrate;
				sp->config.ifgmode = sdl_default_t1_span.ifgmode;
				sp->config.ifgcrc = sdl_default_t1_span.ifgcrc;
				sp->config.ifclock = sp->config.ifclock;
				sp->config.ifcoding = sdl_default_t1_span.ifcoding;
				sp->config.ifframing = sdl_default_t1_span.ifframing;
				/* reconfigure all chans */
				for (chan = 0; chan < 32; chan++) {
					struct ch *ch;

					if ((ch = sp->chans[chan]) == NULL)
						continue;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_E1:
					case SDL_TYPE_J1:
						ch->sdl.config.iftype = sdl_default_t1_span.iftype;
						ch->sdl.config.ifrate = sdl_default_t1_span.ifrate;
						ch->sdl.config.ifblksize = sdl_default_t1_span.ifblksize;
						break;
					case SDL_TYPE_DS0:
					case SDL_TYPE_DS0A:
						ch->sdl.config.ifblksize = sdl_default_t1_chan.ifblksize;
						break;
					}
					ch->sdl.config.ifmode = sdl_default_t1_chan.ifmode;
				}
				break;
			case SDL_GTYPE_J1:
				switch (sp->config.iftype) {
				case SDL_TYPE_E1:
				case SDL_TYPE_T1:
					sp->config.iftype = sdl_default_j1_span.iftype;
					sp->config.ifrate = sdl_default_j1_span.ifrate;
					sp->config.ifblksize = sdl_default_j1_span.ifblksize;
					break;
				case SDL_TYPE_DS0:
					sp->config.iftype = sdl_default_j1_chan.iftype;
					sp->config.ifrate = sdl_default_j1_chan.ifrate;
				case SDL_TYPE_DS0A:
					sp->config.ifblksize = sdl_default_j1_chan.ifblksize;
					break;
				}
				sp->config.ifmode = sdl_default_j1_span.ifmode;
				sp->config.ifgtype = sdl_default_j1_span.ifgtype;
				sp->config.ifgrate = sdl_default_j1_span.ifgrate;
				sp->config.ifgmode = sdl_default_j1_span.ifgmode;
				sp->config.ifgcrc = sdl_default_j1_span.ifgcrc;
				sp->config.ifclock = sp->config.ifclock;
				sp->config.ifcoding = sdl_default_j1_span.ifcoding;
				sp->config.ifframing = sdl_default_j1_span.ifframing;
				/* reconfigure all chans */
				for (chan = 0; chan < 32; chan++) {
					struct ch *ch;

					if ((ch = sp->chans[chan]) == NULL)
						continue;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_T1:
					case SDL_TYPE_E1:
						ch->sdl.config.iftype = sdl_default_j1_span.iftype;
						ch->sdl.config.ifrate = sdl_default_j1_span.ifrate;
						ch->sdl.config.ifblksize = sdl_default_j1_span.ifblksize;
						break;
					case SDL_TYPE_DS0:
						ch->sdl.config.iftype = sdl_default_j1_chan.iftype;
						ch->sdl.config.ifrate = sdl_default_j1_chan.ifrate;
					case SDL_TYPE_DS0A:
						ch->sdl.config.ifblksize = sdl_default_j1_chan.ifblksize;
						break;
					}
					ch->sdl.config.ifmode = sdl_default_j1_chan.ifmode;
				}
				break;
			}
			for (chan = 0; chan < 32; chan++) {
				struct ch *ch;

				if ((ch = sp->chans[chan]) == NULL)
					continue;
				ch->sdl.config.ifgtype = sp->config.ifgtype;
				ch->sdl.config.ifgrate = sp->config.ifgrate;
				ch->sdl.config.ifgmode = sp->config.ifgmode;
				ch->sdl.config.ifgcrc = sp->config.ifgcrc;
				ch->sdl.config.ifclock = sp->config.ifclock;
				ch->sdl.config.ifcoding = sp->config.ifcoding;
				ch->sdl.config.ifframing = sp->config.ifframing;
			}

			xp_span_reconfig(sp);
		}
	}

	cd->plx[INTCSR] = PLX_INTENA;	/* enable interrupts */
	cd->xlb[CTLREG] = cd->ctlreg | (INTENA | DINTENA);	/* enable interrupts and full mode */
}

noinline __unlikely void
xp_t1_card_reconfig(struct cd *cd)
{
	int span, offset;

	/* setup T1 card defaults */
	cd->config = sdl_default_t1_chan;
	/* zero all span registers */
	for (span = 0; span < X400_SPANS; span++)
		for (offset = 0; offset < 160; offset++)
			cd->xlb[(span << 8)
				+ offset] = 0x00;
	/* set up for interleaved serial bus operation, byte mode */
	cd->xlb[0x094] = 0x09;
	cd->xlb[0x194] = 0x08;
	cd->xlb[0x294] = 0x08;
	cd->xlb[0x394] = 0x08;
}

noinline __unlikely void
xp_e1_card_reconfig(struct cd *cd)
{
	unsigned long timeout;
	int span, offset;

	/* setup E1 card defaults */
	cd->config = sdl_default_e1_chan;
	/* zero all span registers */
	for (span = 0; span < X400_SPANS; span++)
		for (offset = 0; offset < 256; offset++)
			cd->xlb[(span << 8)
				+ offset] = 0x00;
	/* set up for interleaved serial bus operation, byte mode */
	cd->xlb[0x0b5] = 0x09;	/* master w/ 3 slaves, 8.192 MHz bus */
	cd->xlb[0x1b5] = 0x08;	/* IBO slave */
	cd->xlb[0x2b5] = 0x08;	/* IBO slave */
	cd->xlb[0x3b5] = 0x08;	/* IBO slave */
	/* line interface reset */
	cd->xlb[0x0aa] = 0x80;	/* LIRST */
	cd->xlb[0x1aa] = 0x80;	/* LIRST */
	cd->xlb[0x2aa] = 0x80;	/* LIRST */
	cd->xlb[0x3aa] = 0x80;	/* LIRST */
	/* wait for 40 ms */
	timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
	while ((volatile unsigned long)
	       jiffies < timeout) ;
	/* release LIRST bit */
	cd->xlb[0x0aa] = 0x80;	/* not LIRST */
	cd->xlb[0x1aa] = 0x80;	/* not LIRST */
	cd->xlb[0x2aa] = 0x80;	/* not LIRST */
	cd->xlb[0x3aa] = 0x80;	/* not LIRST */
	/* rewrite after LIRST */
	cd->xlb[0x0b5] = 0x09;	/* master w/ 3 slaves, 8.192 MHz bus */
	cd->xlb[0x1b5] = 0x08;	/* IBO slave */
	cd->xlb[0x2b5] = 0x08;	/* IBO slave */
	cd->xlb[0x3b5] = 0x08;	/* IBO slave */

	/* FIXME: reconfigure ISR */
	/* FIXME: have to reconfigure all spans now... */
}

noinline __unlikely void
xp_x1_card_reconfig(struct cd *cd)
{
	unsigned long timeout;
	uint8_t reg00, lic2;

	switch (cd->config.ifgtype) {
	case SDL_GTYPE_E1:
		reg00 = 0x02;
		lic2 = 0x98;
		/* setup E1 card defaults */
		cd->config = sdl_default_e1_chan;
		break;
	default:
	case SDL_GTYPE_NONE:
	case SDL_GTYPE_T1:
		reg00 = 0x00;
		lic2 = 0x18;
		/* setup T1 card defaults */
		cd->config = sdl_default_t1_chan;
		break;
	case SDL_GTYPE_J1:
		reg00 = 0x00;
		lic2 = 0x18;
		/* setup J1 card defaults */
		cd->config = sdl_default_j1_chan;
		break;
	}

	/* place all framers in E1, T1 or J1 mode */
	cd->xlb[0x000] = reg00;
	cd->xlb[0x100] = reg00;
	cd->xlb[0x200] = reg00;
	cd->xlb[0x300] = reg00;
	/* set up for interleaved serial bus operation, byte mode */
	cd->xlb[0x0c5] = 0x28 + 0;
	cd->xlb[0x1c5] = 0x28 + 1;
	cd->xlb[0x2c5] = 0x28 + 2;
	cd->xlb[0x3c5] = 0x28 + 3;
	/* release LIRST bit */
	cd->xlb[0x079] = lic2;	/* E1, normal, no LIRST */
	cd->xlb[0x179] = lic2;	/* E1, normal, no LIRST */
	cd->xlb[0x279] = lic2;	/* E1, normal, no LIRST */
	cd->xlb[0x379] = lic2;	/* E1, normal, no LIRST */
	/* line interface reset */
	cd->xlb[0x079] = lic2 | 0x40;	/* E1, normal, LIRST */
	cd->xlb[0x179] = lic2 | 0x40;	/* E1, normal, LIRST */
	cd->xlb[0x279] = lic2 | 0x40;	/* E1, normal, LIRST */
	cd->xlb[0x379] = lic2 | 0x40;	/* E1, normal, LIRST */
	/* wait for 40 ms */
	timeout = (volatile unsigned long) jiffies + 100 * HZ / 1000;
	while ((volatile unsigned long)
	       jiffies < timeout) ;
	/* release LIRST bit */
	cd->xlb[0x079] = lic2;	/* E1, normal, no LIRST */
	cd->xlb[0x179] = lic2;	/* E1, normal, no LIRST */
	cd->xlb[0x279] = lic2;	/* E1, normal, no LIRST */
	cd->xlb[0x379] = lic2;	/* E1, normal, no LIRST */
}

/*
 *  ------------------------------------------------------------------------
 *
 *  Timers
 *
 *  ------------------------------------------------------------------------
 */
enum { tall, t1, t2, t3, t4, t5, t6, t7, t8, t9 };

#if 1
noinline fastcall __hot void
xp_stop_timer(struct ch *ch, toid_t *tidp, uint t)
{
	toid_t tid_old;

	if (likely((tid_old = XCHG(tidp, 0)) != 0)) {
		LOGTE(ch2sid(ch), "-> T%u STOP <-", t);
		spin_unlock(&ch->lock);
		untimeout(tid_old);
		spin_lock(&ch->lock);
	}
}

noinline fastcall __hot void
xp_start_timer(struct ch *ch, toid_t *tidp, timo_fcn_t *fnc, sl_ulong ticks, uint t)
{
	toid_t tid_old;

	if (unlikely((tid_old = XCHG(tidp, 0)) != 0)) {
		LOGTE(ch2sid(ch), "-> T%u STOP <-", t);
		spin_unlock(&ch->lock);
		untimeout(tid_old);
		spin_lock(&ch->lock);
	} else
		LOGTE(ch2sid(ch), "-> T%d START <- (%u hz, HZ is %u)", t, ticks, (uint) HZ);
	*tidp = timeout(fnc, (caddr_t) ch, ticks);
}

#else

static noinline fastcall void
xp_stop_timer_t1(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T1 STOP <-");
	printd(("%s: %p: -> T1 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t1);
}

static noinline fastcall void
xp_start_timer_t1(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T1 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.config.t1),
	      ch->sl.config.t1, (uint) HZ);
	printd(("%s: %p: -> T1 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.config.t1), ch->sl.config.t1, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t1, ch->sl.config.t1);
}

static noinline fastcall void
xp_stop_timer_t2(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T2 STOP <- ");
	printd(("%s: %p: -> T2 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t2);
}

static noinline fastcall void
xp_start_timer_t2(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T2 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.config.t2),
	      ch->sl.config.t2, (uint) HZ);
	printd(("%s: %p: -> T2 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.config.t2), ch->sl.config.t2, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t2, ch->sl.config.t2);
}

static noinline fastcall void
xp_stop_timer_t3(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T3 STOP <-");
	printd(("%s: %p: -> T3 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t3);
}

static noinline fastcall void
xp_start_timer_t3(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T3 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.config.t3),
	      ch->sl.config.t3, (uint) HZ);
	printd(("%s: %p: -> T3 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.config.t3), ch->sl.config.t3, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t3, ch->sl.config.t3);
}

static noinline fastcall void
xp_stop_timer_t4(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T4 STOP <-");
	printd(("%s: %p: -> T4 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t4);
}

static noinline fastcall void
xp_start_timer_t4(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T4 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.statem.t4v),
	      ch->sl.statem.t4v, (uint) HZ);
	printd(("%s: %p: -> T4 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.statem.t4v), ch->sl.statem.t4v, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t4, ch->sl.statem.t4v);
}

static inline fastcall __hot_out void
xp_stop_timer_t5(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T5 STOP <-");
	printd(("%s: %p: -> T5 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t5);
}

static inline fastcall __hot_out void
xp_start_timer_t5(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T5 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.config.t5),
	      ch->sl.config.t5, (uint) HZ);
	printd(("%s: %p: -> T5 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.config.t5), ch->sl.config.t5, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t5, ch->sl.config.t5);
}

static inline fastcall __hot_in void
xp_stop_timer_t6(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T6 STOP <-");
	printd(("%s: %p: -> T6 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t6);
}

static inline fastcall __hot_in void
xp_start_timer_t6(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T6 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.config.t6),
	      ch->sl.config.t6, (uint) HZ);
	printd(("%s: %p: -> T6 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.config.t6), ch->sl.config.t6, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t6, ch->sl.config.t6);
}

static inline fastcall __hot_in void
xp_stop_timer_t7(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T7 STOP <-");
	printd(("%s: %p: -> T7 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sl.timers.t7);
}

static inline fastcall __hot_in void
xp_start_timer_t7(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T7 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sl.config.t7),
	      ch->sl.config.t7, (uint) HZ);
	printd(("%s: %p: -> T7 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sl.config.t7), ch->sl.config.t7, (uint) HZ));
	mi_timer_MAC(ch->sl.timers.t7, ch->sl.config.t7);
}

static inline fastcall __hot_in void
xp_stop_timer_t8(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T8 STOP <-");
	printd(("%s: %p: -> T8 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sdt.timers.t8);
}

static inline fastcall __hot_in void
xp_start_timer_t8(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T8 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sdt.config.t8),
	      ch->sdt.config.t8, (uint) HZ);
	printd(("%s: %p: -> T8 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sdt.config.t8), ch->sdt.config.t8, (uint) HZ));
	mi_timer_MAC(ch->sdt.timers.t8, ch->sdt.config.t8);
}

#if 0
static void
xp_stop_timer_t9(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T9 STOP <-");
	printd(("%s: %p: -> T9 STOP <-\n", DRV_NAME, ch));
	mi_timer_stop(ch->sdl.timers.t9);
}

static void
xp_start_timer_t9(struct ch *ch)
{
	LOGTE(ch2sid(ch), "-> T9 START <- (%lu hz, %u msec, HZ is %u)", drv_msectohz(ch->sdl.config.t9),
	      ch->sdl.config.t9, (uint) HZ);
	printd(("%s: %p: -> T9 START <- (%lu hz, %u msec, HZ is %u)\n", DRV_NAME, ch,
		drv_msectohz(ch->sdl.config.t9), ch->sdl.config.t9, (uint) HZ));
	mi_timer_MAC(ch->sdl.timers.t9, ch->ch->sdl.config.t9);
}
#endif

#endif

#if 1
static inline fastcall __hot void
xp_timer_stop(struct ch *ch, const uint t)
{
	int single = 1;

	switch (t) {
	case tall:
		single = 0;
		/* fall through */
	case t1:
		xp_stop_timer(ch, &ch->sl.timers.t1, t1);
		if (single)
			break;
		/* fall through */
	case t2:
		xp_stop_timer(ch, &ch->sl.timers.t2, t2);
		if (single)
			break;
		/* fall through */
	case t3:
		xp_stop_timer(ch, &ch->sl.timers.t3, t3);
		if (single)
			break;
		/* fall through */
	case t4:
		xp_stop_timer(ch, &ch->sl.timers.t4, t4);
		if (single)
			break;
		/* fall through */
	case t5:
		xp_stop_timer(ch, &ch->sl.timers.t5, t5);
		if (single)
			break;
		/* fall through */
	case t6:
		xp_stop_timer(ch, &ch->sl.timers.t6, t6);
		if (single)
			break;
		/* fall through */
	case t7:
		xp_stop_timer(ch, &ch->sl.timers.t7, t7);
		if (single)
			break;
		/* fall through */
	case t8:
		xp_stop_timer(ch, &ch->sdt.timers.t8, t8);
		if (single)
			break;
		/* fall through */
#if 0
	case t9:
		xp_stop_timer(ch, &ch->sdl.timers.t9, t9);
		if (single)
			break;
		/* fall through */
#endif
		break;
	default:
		__swerr();
		break;
	}
}
#else
static inline fastcall __hot void
xp_timer_stop(struct ch *ch, const uint t)
{
	int single = 1;

	switch (t) {
	case tall:
		single = 0;
		/* fall through */
	case t1:
		xp_stop_timer_t1(ch);
		if (single)
			break;
		/* fall through */
	case t2:
		xp_stop_timer_t2(ch);
		if (single)
			break;
		/* fall through */
	case t3:
		xp_stop_timer_t3(ch);
		if (single)
			break;
		/* fall through */
	case t4:
		xp_stop_timer_t4(ch);
		if (single)
			break;
		/* fall through */
	case t5:
		xp_stop_timer_t5(ch);
		if (single)
			break;
		/* fall through */
	case t6:
		xp_stop_timer_t6(ch);
		if (single)
			break;
		/* fall through */
	case t7:
		xp_stop_timer_t7(ch);
		if (single)
			break;
		/* fall through */
	case t8:
		xp_stop_timer_t8(ch);
		if (single)
			break;
		/* fall through */
#if 0
	case t9:
		xp_stop_timer_t9(ch);
		if (single)
			break;
		/* fall through */
#endif
		break;
	default:
		swerr();
		break;
	}
}
#endif

#if 1
streamscall void xp_t1_expiry(caddr_t);
streamscall void xp_t2_expiry(caddr_t);
streamscall void xp_t3_expiry(caddr_t);
streamscall void xp_t4_expiry(caddr_t);
streamscall void xp_t5_expiry(caddr_t);
streamscall void xp_t6_expiry(caddr_t);
streamscall void xp_t7_expiry(caddr_t);
streamscall void xp_t8_expiry(caddr_t);

#if 0
streamscall void xp_t9_expiry(caddr_t);
#endif

static inline fastcall __hot void
xp_timer_start(struct ch *ch, const uint t)
{
	switch (t) {
	case t1:
		xp_start_timer(ch, &ch->sl.timers.t1, &xp_t1_expiry, ch->sl.config.t1, t);
		break;
	case t2:
		xp_start_timer(ch, &ch->sl.timers.t2, &xp_t2_expiry, ch->sl.config.t2, t);
		break;
	case t3:
		xp_start_timer(ch, &ch->sl.timers.t3, &xp_t3_expiry, ch->sl.config.t3, t);
		break;
	case t4:
		xp_start_timer(ch, &ch->sl.timers.t4, &xp_t4_expiry, ch->sl.statem.t4v, t);
		break;
	case t5:
		xp_start_timer(ch, &ch->sl.timers.t5, &xp_t5_expiry, ch->sl.config.t5, t);
		break;
	case t6:
		xp_start_timer(ch, &ch->sl.timers.t6, &xp_t6_expiry, ch->sl.config.t6, t);
		break;
	case t7:
		xp_start_timer(ch, &ch->sl.timers.t7, &xp_t7_expiry, ch->sl.config.t7, t);
		break;
	case t8:
		xp_start_timer(ch, &ch->sdt.timers.t8, &xp_t8_expiry, ch->sdt.config.t8, t);
		break;
#if 0
	case t9:
		xp_start_timer(ch, &ch->sdl.timers.t9, &xp_t9_expiry, ch->sdl.config.t9, t9);
		break;
#endif
	default:
		__swerr();
		break;
	}
}
#else
static inline fastcall __hot void
xp_timer_start(struct ch *ch, const uint t)
{
	xp_timer_stop(ch, t);
	switch (t) {
	case t1:
		xp_start_timer_t1(ch);
		break;
	case t2:
		xp_start_timer_t2(ch);
		break;
	case t3:
		xp_start_timer_t3(ch);
		break;
	case t4:
		xp_start_timer_t4(ch);
		break;
	case t5:
		xp_start_timer_t5(ch);
		break;
	case t6:
		xp_start_timer_t6(ch);
		break;
	case t7:
		xp_start_timer_t7(ch);
		break;
	case t8:
		xp_start_timer_t8(ch);
		break;
#if 0
	case t9:
		xp_start_timer_t9(ch);
		break;
#endif
	default:
		swerr();
		break;
	}
}
#endif

#if 0
noinline __unlikely void
xp_free_timers(struct ch *ch)
{
	mblk_t *tp;

	if ((tp = XCHG(&ch->sl.timers.t1, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sl.timers.t2, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sl.timers.t3, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sl.timers.t4, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sl.timers.t5, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sl.timers.t6, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sl.timers.t7, NULL)))
		mi_timer_free(tp);
	if ((tp = XCHG(&ch->sdt.timers.t8, NULL)))
		mi_timer_free(tp);
#if 0
	if ((tp = XCHG(&ch->sdl.timers.t9, NULL)))
		mi_timer_free(tp);
#endif
}
#endif

#if 0
noinline __unlikely int
xp_alloc_timers(struct ch *ch, queue_t *q)
{
	mblk_t *tp;

	/* SDL timer allocation */
#if 0
	if (!(tp = ch->sdl.timers.t9 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t9;
#endif
	/* SDT timer allocation */
	if (!(tp = ch->sdt.timers.t8 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t8;
	/* SL timer allocation */
	if (!(tp = ch->sl.timers.t7 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t7;
	if (!(tp = ch->sl.timers.t6 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t6;
	if (!(tp = ch->sl.timers.t5 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t5;
	if (!(tp = ch->sl.timers.t4 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t4;
	if (!(tp = ch->sl.timers.t3 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t3;
	if (!(tp = ch->sl.timers.t2 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t2;
	if (!(tp = ch->sl.timers.t1 = mi_timer_alloc_MAC(q, sizeof(int))))
		goto enobufs;
	*(int *) tp->b_rptr = t1;
	return (0);
      enobufs:
	xp_free_timers(ch);
	return (-ENOBUFS);
}
#endif

/*
 *  -------------------------------------------------------------------------
 *
 *  Duration Statistics
 *
 *  -------------------------------------------------------------------------
 *
 *  I don't really know what needs to be done with these now.  I had them
 *  working at some point, but now they are disconnected.  These are for SS7
 *  duration statistics mandated by Q.752.2.  TODO: These need to be hooked in
 *  somehow.
 */
#if 0
noinline fastcall __unlikely void
sl_is_stats(queue_t *q)
{
	struct xp *xp = XP_PRIV(q);
	struct ch *ch = xp->ch;

	if (ch->sl.stamp.sl_dur_unavail)
		ch->sl.stats.sl_dur_unavail += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail, 0);
	if (ch->sl.stamp.sl_dur_unavail_rpo)
		ch->sl.stats.sl_dur_unavail_rpo += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail_rpo, 0);
	if (ch->sl.stamp.sl_dur_unavail_failed)
		ch->sl.stats.sl_dur_unavail_failed += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail_failed, 0);
	ch->sl.stamp.sl_dur_in_service = jiffies;
}

noinline fastcall __unlikely void
sl_oos_stats(queue_t *q)
{
	struct xp *xp = XP_PRIV(q);
	struct ch *ch = xp->ch;

	if (ch->sl.stamp.sl_dur_in_service)
		ch->sl.stats.sl_dur_in_service += jiffies - xchg(&ch->sl.stamp.sl_dur_in_service, 0);
	if (ch->sl.stamp.sl_dur_unavail_rpo)
		ch->sl.stats.sl_dur_unavail_rpo += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail_rpo, 0);
	if (ch->sl.stamp.sl_dur_unavail_failed)
		ch->sl.stats.sl_dur_unavail_failed += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail_failed, 0);
	ch->sl.stamp.sl_dur_unavail = jiffies;
}

noinline fastcall __unlikely void
sl_rpo_stats(queue_t *q)
{
	struct xp *xp = XP_PRIV(q);
	struct ch *ch = xp->ch;

	if (ch->sl.stamp.sl_dur_unavail_rpo)
		ch->sl.stats.sl_dur_unavail_rpo += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail_rpo, 0);
}

noinline fastcall __unlikely void
sl_rpr_stats(queue_t *q)
{
	struct xp *xp = XP_PRIV(q);
	struct ch *ch = xp->ch;

	if (ch->sl.stamp.sl_dur_unavail_rpo)
		ch->sl.stats.sl_dur_unavail_rpo += jiffies - xchg(&ch->sl.stamp.sl_dur_unavail_rpo, 0);
}
#endif

/*
 *  -------------------------------------------------------------------------
 *
 *  SL State Machines
 *
 *  -------------------------------------------------------------------------
 */
#define SN_OUTSIDE(lower,middle,upper) \
	(  ( (lower) <= (upper) ) \
	   ? ( ( (middle) < (lower) ) || ( (middle) > (upper) ) ) \
	   : ( ( (middle) < (lower) ) && ( (middle) > (upper) ) ) \
	   )

/*
 *  -----------------------------------------------------------------------
 *
 *  STATE MACHINES:- The order of the state machine primitives below may seem somewhat disorganized at first
 *  glance; however, they have been ordered by dependency because they are all inline functions.  You see, the L2
 *  state machine does not required multiple threading because there is never a requirement to invoke the
 *  individual state machines concurrently.  This works out good for the driver, because a primitive action
 *  expands inline to the necessary procedure, while the source still takes the appearance of the SDL diagrams in
 *  the SS7 specification for inspection and debugging.
 *
 *  -----------------------------------------------------------------------
 */

#define sl_cc_stop sl_cc_normal
noinline fastcall void
sl_cc_normal(struct ch *ch)
{
	xp_timer_stop(ch, t5);
	ch->sl.statem.cc_state = SL_STATE_IDLE;
}

noinline fastcall void
sl_rc_stop(struct ch *ch)
{
	sl_cc_normal(ch);
	ch->sl.statem.rc_state = SL_STATE_IDLE;
}

noinline fastcall void
sl_aerm_stop(struct ch *ch)
{
	ch->sdt.statem.aerm_state = SDT_STATE_IDLE;
	ch->sdt.statem.Ti = ch->sdt.config.Tin;
}

noinline fastcall void
sl_iac_stop(struct ch *ch)
{
	if (ch->sl.statem.iac_state != SL_STATE_IDLE) {
		xp_timer_stop(ch, t3);
		xp_timer_stop(ch, t2);
		xp_timer_stop(ch, t4);
		sl_aerm_stop(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.iac_state = SL_STATE_IDLE;
	}
}

noinline fastcall void
sl_txc_send_sios(struct ch *ch)
{
	xp_timer_stop(ch, t7);
	if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
	    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
		xp_timer_stop(ch, t6);
	ch->sl.statem.lssu_available = 1;
	ch->sl.statem.tx.sio = LSSU_SIOS;
}

noinline fastcall void
sl_poc_stop(struct ch *ch)
{
	ch->sl.statem.poc_state = SL_STATE_IDLE;
}

noinline fastcall void
sl_eim_stop(struct ch *ch)
{
	ch->sdt.statem.eim_state = SDT_STATE_IDLE;
	xp_timer_stop(ch, t8);
}

noinline fastcall void
sl_suerm_stop(struct ch *ch)
{
	sl_eim_stop(ch);
	ch->sdt.statem.suerm_state = SDT_STATE_IDLE;
}

noinline fastcall void
sl_lsc_link_failure(struct ch *ch, sl_ulong reason)
{
	if (ch->sl.statem.lsc_state != SL_STATE_OUT_OF_SERVICE) {
		ch->sl.statem.failure_reason = reason;
		sl_out_of_service_ind(ch);
		sl_iac_stop(ch);	/* ok if not aligning */
		xp_timer_stop(ch, t1);	/* ok if not running */
		sl_suerm_stop(ch);	/* ok if not running */
		sl_rc_stop(ch);
		sl_txc_send_sios(ch);
		sl_poc_stop(ch);	/* ok if not ITUT */
		ch->sl.statem.emergency = 0;
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.remote_processor_outage = 0;	/* ok if not ANSI */
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
	}
}

noinline fastcall void
sl_txc_send_sib(struct ch *ch)
{
	ch->sl.statem.tx.sio = LSSU_SIB;
	ch->sl.statem.lssu_available = 1;
}

noinline fastcall void
sl_txc_send_sipo(struct ch *ch)
{
	xp_timer_stop(ch, t7);
	if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
	    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
		xp_timer_stop(ch, t6);
	ch->sl.statem.tx.sio = LSSU_SIPO;
	ch->sl.statem.lssu_available = 1;
}

noinline fastcall void
sl_txc_send_sio(struct ch *ch)
{
	ch->sl.statem.tx.sio = LSSU_SIO;
	ch->sl.statem.lssu_available = 1;
}

noinline fastcall void
sl_txc_send_sin(struct ch *ch)
{
	ch->sl.statem.tx.sio = LSSU_SIN;
	ch->sl.statem.lssu_available = 1;
}

noinline fastcall void
sl_txc_send_sie(struct ch *ch)
{
	ch->sl.statem.tx.sio = LSSU_SIE;
	ch->sl.statem.lssu_available = 1;
}

static inline fastcall __hot_write void
sl_txc_send_msu(struct ch *ch)
{
	if (ch->sl.rtb.q_count)
		xp_timer_start(ch, t7);
	ch->sl.statem.msu_inhibited = 0;
	ch->sl.statem.lssu_available = 0;
}

noinline fastcall void
sl_txc_send_fisu(struct ch *ch)
{
	xp_timer_stop(ch, t7);
	if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
	    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
		if (!(ch->option.popt & SS7_POPT_PCR))
			xp_timer_stop(ch, t6);
	ch->sl.statem.msu_inhibited = 1;
	ch->sl.statem.lssu_available = 0;
}

static inline fastcall void
sl_txc_fsnx_value(struct ch *ch)
{
	if (ch->sl.statem.tx.X.fsn != ch->sl.statem.rx.X.fsn)
		ch->sl.statem.tx.X.fsn = ch->sl.statem.rx.X.fsn;
}

noinline fastcall void
sl_txc_nack_to_be_sent(struct ch *ch)
{
	ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib ? 0 : ch->sl.statem.ib_mask;
}

noinline fastcall __unlikely void
sl_lsc_rtb_cleared(struct ch *ch)
{
	if (ch->sl.statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE) {
		ch->sl.statem.remote_processor_outage = 0;
		if (ch->sl.statem.local_processor_outage)
			return;
		sl_remote_processor_recovered_ind(ch);
		sl_rtb_cleared_ind(ch, NULL, NULL);
		sl_txc_send_msu(ch);
		ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
	}
}

STATIC fastcall __hot_write int sl_check_congestion(struct ch *ch, queue_t *q, mblk_t *mp);

static inline fastcall __hot void
sl_txc_bsnr_and_bibr(struct ch *ch)
{
	int pcr = ch->option.popt & SS7_POPT_PCR;

	ch->sl.statem.tx.R.bsn = ch->sl.statem.rx.R.bsn;
	ch->sl.statem.tx.R.bib = ch->sl.statem.rx.R.bib;
	if (ch->sl.statem.clear_rtb) {
		bufq_purge(&ch->sl.rtb);
		ch->sl.statem.Ct = 0;
		sl_check_congestion(ch, NULL, NULL);
		ch->sl.statem.tx.F.fsn = (ch->sl.statem.tx.R.bsn + 1) & ch->sl.statem.sn_mask;
		ch->sl.statem.tx.L.fsn = ch->sl.statem.tx.R.bsn;
		ch->sl.statem.tx.N.fsn = ch->sl.statem.tx.R.bsn;
		ch->sl.statem.tx.N.fib = ch->sl.statem.tx.R.bib;
		ch->sl.statem.Z = (ch->sl.statem.tx.R.bsn + 1) & ch->sl.statem.sn_mask;
		ch->sl.statem.z_ptr = NULL;
		/* FIXME: handle error return */
		sl_lsc_rtb_cleared(ch);
		ch->sl.statem.clear_rtb = 0;
		ch->sl.statem.rtb_full = 0;
		return;
	}
	if (ch->sl.statem.tx.F.fsn != ((ch->sl.statem.tx.R.bsn + 1) & ch->sl.statem.sn_mask)) {
		if (ch->sl.statem.sib_received) {
			ch->sl.statem.sib_received = 0;
			xp_timer_stop(ch, t6);
		}
		do {
			freemsg(bufq_dequeue(&ch->sl.rtb));
			ch->sl.statem.Ct--;
			ch->sl.statem.tx.F.fsn = (ch->sl.statem.tx.F.fsn + 1) & ch->sl.statem.sn_mask;
		} while (ch->sl.statem.tx.F.fsn != ((ch->sl.statem.tx.R.bsn + 1) & ch->sl.statem.sn_mask));
		sl_check_congestion(ch, NULL, NULL);
		if (ch->sl.rtb.q_count == 0)
			xp_timer_stop(ch, t7);
		else
			xp_timer_start(ch, t7);
		if (!pcr || (ch->sl.rtb.q_msgs < ch->sl.config.N1 && ch->sl.rtb.q_count < ch->sl.config.N2))
			ch->sl.statem.rtb_full = 0;
		if (SN_OUTSIDE(ch->sl.statem.tx.F.fsn, ch->sl.statem.Z, ch->sl.statem.tx.L.fsn)
		    || !ch->sl.rtb.q_count) {
			ch->sl.statem.Z = ch->sl.statem.tx.F.fsn;
			ch->sl.statem.z_ptr = ch->sl.rtb.q_head;
		}
	}
	if (pcr)
		return;
	if (ch->sl.statem.tx.N.fib != ch->sl.statem.tx.R.bib) {
		if (ch->sl.statem.sib_received) {
			ch->sl.statem.sib_received = 0;
			xp_timer_stop(ch, t6);
		}
		ch->sl.statem.tx.N.fib = ch->sl.statem.tx.R.bib;
		ch->sl.statem.tx.N.fsn = (ch->sl.statem.tx.F.fsn - 1) & ch->sl.statem.sn_mask;
		if ((ch->sl.statem.z_ptr = ch->sl.rtb.q_head) != NULL)
			ch->sl.statem.retrans_cycle = 1;
		return;
	}
}

noinline fastcall void
sl_txc_sib_received(struct ch *ch)
{
	/* FIXME: consider these variations for all */
	if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
	    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
		if (ch->sl.statem.lssu_available && ch->sl.statem.tx.sio != LSSU_SIB)
			return;
	if (ch->option.pvar != SS7_PVAR_ITUT_93)
		if (!ch->sl.rtb.q_count)
			return;
	if (!ch->sl.statem.sib_received) {
		xp_timer_start(ch, t6);
		ch->sl.statem.sib_received = 1;
	}
	xp_timer_start(ch, t7);
}

noinline fastcall __unlikely void
sl_txc_clear_rtb(struct ch *ch, queue_t *q, mblk_t *mp)
{
	bufq_purge(&ch->sl.rtb);
	ch->sl.statem.Ct = 0;
	ch->sl.statem.clear_rtb = 1;
	ch->sl.statem.rtb_full = 0;	/* added */
	/* FIXME: should probably follow more of the ITUT flush_buffers stuff like reseting Z and FSNF, FSNL, FSNT. */
	sl_check_congestion(ch, q, mp);
}

noinline fastcall __unlikely void
sl_txc_clear_tb(struct ch *ch, queue_t *q, mblk_t *mp)
{
	bufq_purge(&ch->sl.tb);
	sl_flush_wq(ch);
	ch->sl.statem.Cm = 0;
	sl_check_congestion(ch, q, mp);
}

noinline fastcall __unlikely void
sl_txc_flush_buffers(struct ch *ch)
{
	bufq_purge(&ch->sl.rtb);
	ch->sl.statem.rtb_full = 0;
	ch->sl.statem.Ct = 0;
	bufq_purge(&ch->sl.tb);
	sl_flush_wq(ch);
	ch->sl.statem.Cm = 0;
	ch->sl.statem.Z = 0;
	ch->sl.statem.z_ptr = NULL;
	/* Z =0 error in ITUT 93 and ANSI */
	ch->sl.statem.Z = ch->sl.statem.tx.F.fsn = (ch->sl.statem.tx.R.bsn + 1) & ch->sl.statem.sn_mask;
	ch->sl.statem.tx.L.fsn = ch->sl.statem.rx.R.bsn;
	ch->sl.statem.rx.T.fsn = ch->sl.statem.rx.R.bsn;
	xp_timer_stop(ch, t7);
	return;
}

static inline fastcall __hot_in void
sl_rc_fsnt_value(struct ch *ch)
{
	ch->sl.statem.rx.T.fsn = ch->sl.statem.tx.N.fsn;
}

noinline fastcall __unlikely int
sl_txc_retrieval_request_and_fsnc(struct ch *ch, queue_t *q, mblk_t *rp, sl_ulong fsnc)
{
	mblk_t *mp;
	int err;

	ch->sl.statem.tx.C.fsn = fsnc & (ch->sl.statem.sn_mask);
	/* 
	 *  FIXME: Q.704/5.7.2 states:
	 *
	 *  5.7.2   If a changeover order or acknowledgement containing an unreasonable value of the forward
	 *  sequence number is received, no buffer updating or retrieval is performed, and new traffic is started
	 *  on the alternative signalling link(s).
	 *
	 *  It will be necessary to check FSNC for "reasonableness" here and flush RTB and TB and return
	 *  retrieval-complete indication with a return code of "unreasonable FSNC".
	 *
	 *  (Tell the SIGTRAN working group and M2UA guys about this!)
	 */
	while (ch->sl.rtb.q_count && ch->sl.statem.tx.F.fsn != ((fsnc + 1) & ch->sl.statem.sn_mask)) {
		freemsg(bufq_dequeue(&ch->sl.rtb));
		ch->sl.statem.Ct--;
		ch->sl.statem.tx.F.fsn = (ch->sl.statem.tx.F.fsn + 1) & ch->sl.statem.sn_mask;
	}
	while ((mp = bufq_dequeue(&ch->sl.tb))) {
		ch->sl.statem.Cm--;
		bufq_queue(&ch->sl.rtb, mp);
		ch->sl.statem.Ct++;
		if (!ch->sl.statem.Cm)
			qenable(WR(q));
	}
	ch->sl.statem.Z = ch->sl.statem.tx.F.fsn = (ch->sl.statem.tx.C.fsn + 1) & ch->sl.statem.sn_mask;
	while ((mp = bufq_dequeue(&ch->sl.rtb))) {
		ch->sl.statem.Ct--;
		if ((err = sl_retrieved_message_ind(ch, q, mp))) {
			/* FIXME: put mp back on queue or we leak a buffer */
			return (err);
		}
	}
	ch->sl.statem.rtb_full = 0;
	sl_retrieval_complete_ind(ch, q, rp);
	ch->sl.statem.Cm = 0;
	ch->sl.statem.Ct = 0;
	ch->sl.statem.tx.N.fsn = ch->sl.statem.tx.L.fsn = ch->sl.statem.tx.C.fsn;
	return (0);
}

static inline fastcall __hot_write void
sl_daedt_fisu(struct ch *ch, mblk_t *mp)
{
	if (ch->option.popt & SS7_POPT_XSN) {
		*(sl_ushort *) mp->b_wptr = htons(ch->sl.statem.tx.N.bsn | ch->sl.statem.tx.N.bib);
		mp->b_wptr += sizeof(sl_ushort);
		*(sl_ushort *) mp->b_wptr = htons(ch->sl.statem.tx.N.fsn | ch->sl.statem.tx.N.fib);
		mp->b_wptr += sizeof(sl_ushort);
		*(sl_ushort *) mp->b_wptr = 0;
		mp->b_wptr += sizeof(sl_ushort);
	} else {
		*(sl_uchar *) mp->b_wptr = (ch->sl.statem.tx.N.bsn | ch->sl.statem.tx.N.bib);
		mp->b_wptr += sizeof(sl_uchar);
		*(sl_uchar *) mp->b_wptr = (ch->sl.statem.tx.N.fsn | ch->sl.statem.tx.N.fib);
		mp->b_wptr += sizeof(sl_uchar);
		*(sl_uchar *) mp->b_wptr = 0;
		mp->b_wptr += sizeof(sl_uchar);
	}
}

noinline fastcall __hot_write void
sl_daedt_lssu(struct ch *ch, mblk_t *mp)
{
	if (ch->option.popt & SS7_POPT_XSN) {
		*(sl_ushort *) mp->b_wptr = htons(ch->sl.statem.tx.N.bsn | ch->sl.statem.tx.N.bib);
		mp->b_wptr += sizeof(sl_ushort);
		*(sl_ushort *) mp->b_wptr = htons(ch->sl.statem.tx.N.fsn | ch->sl.statem.tx.N.fib);
		mp->b_wptr += sizeof(sl_ushort);
		*(sl_ushort *) mp->b_wptr = htons(1);
		mp->b_wptr += sizeof(sl_ushort);
	} else {
		*(sl_uchar *) mp->b_wptr = (ch->sl.statem.tx.N.bsn | ch->sl.statem.tx.N.bib);
		mp->b_wptr += sizeof(sl_uchar);
		*(sl_uchar *) mp->b_wptr = (ch->sl.statem.tx.N.fsn | ch->sl.statem.tx.N.fib);
		mp->b_wptr += sizeof(sl_uchar);
		*(sl_uchar *) mp->b_wptr = 1;
		mp->b_wptr += sizeof(sl_uchar);
	}
	*(sl_uchar *) mp->b_wptr = (ch->sl.statem.tx.sio);
	mp->b_wptr += sizeof(sl_uchar);
}

static inline fastcall __hot_write void
sl_daedt_msu(struct ch *ch, mblk_t *mp)
{
	int len = msgdsize(mp);

	if (ch->option.popt & SS7_POPT_XSN) {
		((sl_ushort *) mp->b_rptr)[0] = htons(ch->sl.statem.tx.N.bsn | ch->sl.statem.tx.N.bib);
		((sl_ushort *) mp->b_rptr)[1] = htons(ch->sl.statem.tx.N.fsn | ch->sl.statem.tx.N.fib);
		((sl_ushort *) mp->b_rptr)[2] = htons(len - 6 < 512 ? len - 6 : 511);
	} else {
		((sl_uchar *) mp->b_rptr)[0] = (ch->sl.statem.tx.N.bsn | ch->sl.statem.tx.N.bib);
		((sl_uchar *) mp->b_rptr)[1] = (ch->sl.statem.tx.N.fsn | ch->sl.statem.tx.N.fib);
		((sl_uchar *) mp->b_rptr)[2] = (len - 3 < 64 ? len - 3 : 63);
	}
}

static inline fastcall __hot_out mblk_t *
sl_txc_transmission_request(struct ch *ch)
{
	mblk_t *mp = NULL;
	int pcr;

	if (ch->sl.statem.txc_state != SL_STATE_IN_SERVICE)
		return (mp);
	pcr = ch->option.popt & SS7_POPT_PCR;
	if (ch->sl.statem.lssu_available) {
		if ((mp = ss7_fast_allocb(&xp_bufpool, 7, BPRI_HI))) {
			if (ch->sl.statem.tx.sio == LSSU_SIB) {
				ch->sl.stats.sl_sibs_sent++;
				ch->sl.statem.lssu_available = 0;
			}
			ch->sl.statem.tx.N.fsn = ch->sl.statem.tx.L.fsn;
			ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib;
			ch->sl.statem.tx.N.bsn = (ch->sl.statem.tx.X.fsn - 1) & ch->sl.statem.sn_mask;
			ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.fib;
			sl_daedt_lssu(ch, mp);
		}
		return (mp);
	}
	if (ch->sl.statem.msu_inhibited) {
		if ((mp = ss7_fast_allocb(&xp_bufpool, 6, BPRI_HI))) {
			ch->sl.statem.tx.N.fsn = ch->sl.statem.tx.L.fsn;
			ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib;
			ch->sl.statem.tx.N.bsn = (ch->sl.statem.tx.X.fsn - 1) & ch->sl.statem.sn_mask;
			ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.fib;
			sl_daedt_fisu(ch, mp);
		}
		return (mp);
	}
	if (pcr) {
		if ((ch->sl.rtb.q_msgs < ch->sl.config.N1) && (ch->sl.rtb.q_count < ch->sl.config.N2)) {
			ch->sl.statem.forced_retransmission = 0;
			ch->sl.statem.rtb_full = 0;
		}
		if (ch->sl.tb.q_count && ch->sl.statem.rtb_full)
			ch->sl.statem.forced_retransmission = 1;
	}
	if ((!pcr && ch->sl.statem.retrans_cycle)
	    || (pcr && (ch->sl.statem.forced_retransmission || (!ch->sl.tb.q_count && ch->sl.rtb.q_count)))) {
		mblk_t *bp;

		if ((bp = ch->sl.statem.z_ptr) && !(mp = dupmsg(bp)))
			return (mp);
		if (!bp && pcr) {
			if ((bp = ch->sl.statem.z_ptr = ch->sl.rtb.q_head) && !(mp = dupmsg(bp)))
				return (mp);
			ch->sl.statem.Z = ch->sl.statem.tx.F.fsn;
		}
		if (mp) {
			ch->sl.statem.z_ptr = bp->b_next;
			if (pcr) {
				ch->sl.statem.tx.N.fsn = ch->sl.statem.Z;
				ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.fib;
				ch->sl.statem.tx.N.bsn = (ch->sl.statem.tx.X.fsn - 1) & ch->sl.statem.sn_mask;
				ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib;
				ch->sl.statem.Z = (ch->sl.statem.Z + 1) & ch->sl.statem.sn_mask;
			} else {
				ch->sl.statem.tx.N.fsn = (ch->sl.statem.tx.N.fsn + 1) & ch->sl.statem.sn_mask;
				ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.fib;
				ch->sl.statem.tx.N.bsn = (ch->sl.statem.tx.X.fsn - 1) & ch->sl.statem.sn_mask;
				ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib;
			}
			sl_daedt_msu(ch, mp);
			if (ch->sl.statem.tx.N.fsn == ch->sl.statem.tx.L.fsn || ch->sl.statem.z_ptr == NULL)
				ch->sl.statem.retrans_cycle = 0;
		}
		return (mp);
	}
	if ((!pcr && (!ch->sl.tb.q_count || ch->sl.statem.rtb_full))
	    || (pcr && (!ch->sl.tb.q_count && !ch->sl.rtb.q_count))) {
		if ((mp = ss7_fast_allocb(&xp_bufpool, 6, BPRI_HI))) {
			ch->sl.statem.tx.N.fsn = ch->sl.statem.tx.L.fsn;
			ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib;
			ch->sl.statem.tx.N.bsn = (ch->sl.statem.tx.X.fsn - 1) & ch->sl.statem.sn_mask;
			ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.fib;
			sl_daedt_fisu(ch, mp);
		}
		return (mp);
	} else {
		spin_lock(&ch->sl.tb.q_lock);
		if ((mp = bufq_head(&ch->sl.tb)) && (mp = dupmsg(mp))) {
			mblk_t *bp = __bufq_dequeue(&ch->sl.tb);

			spin_unlock(&ch->sl.tb.q_lock);
			ch->sl.statem.Cm--;
			if (!ch->sl.statem.Cm)
				sl_enable_rq(ch);
			ch->sl.statem.tx.L.fsn = (ch->sl.statem.tx.L.fsn + 1) & ch->sl.statem.sn_mask;
			ch->sl.statem.tx.N.fsn = ch->sl.statem.tx.L.fsn;
			if (!ch->sl.rtb.q_count)
				xp_timer_start(ch, t7);
			bufq_queue(&ch->sl.rtb, bp);
			ch->sl.statem.Ct++;
			sl_rc_fsnt_value(ch);
			if (pcr) {
				if ((ch->sl.rtb.q_msgs >= ch->sl.config.N1)
				    || (ch->sl.rtb.q_count >= ch->sl.config.N2)) {
					ch->sl.statem.rtb_full = 1;
					ch->sl.statem.forced_retransmission = 1;
				}
			} else {
				if ((ch->sl.rtb.q_msgs >= ch->sl.config.N1)
				    || (ch->sl.rtb.q_count >= ch->sl.config.N2)
				    || (ch->sl.statem.tx.L.fsn ==
					((ch->sl.statem.tx.F.fsn - 2) & ch->sl.statem.sn_mask)))
					ch->sl.statem.rtb_full = 1;
			}
			ch->sl.statem.tx.N.bib = ch->sl.statem.tx.N.bib;
			ch->sl.statem.tx.N.bsn = (ch->sl.statem.tx.X.fsn - 1) & ch->sl.statem.sn_mask;
			ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.fib;
			sl_daedt_msu(ch, mp);
		} else
			spin_unlock(&ch->sl.tb.q_lock);
		return (mp);
	}
}

noinline fastcall __unlikely void
sl_daedr_start(struct ch *ch)
{
	ch->sdt.statem.daedr_state = SDT_STATE_IN_SERVICE;
	ch->sdl.statem.rx_state = SDL_STATE_IN_SERVICE;
	ch->sdl.config.ifflags |= (SDL_IF_UP | SDL_IF_RX_RUNNING);
}

noinline fastcall __unlikely void
sl_rc_start(struct ch *ch)
{
	if (ch->sl.statem.rc_state == SL_STATE_IDLE) {
		ch->sl.statem.rx.X.fsn = 0;
		ch->sl.statem.rx.X.fib = ch->sl.statem.ib_mask;
		ch->sl.statem.rx.F.fsn = 0;
		ch->sl.statem.rx.T.fsn = ch->sl.statem.sn_mask;
		ch->sl.statem.rtr = 0;	/* Basic only (note 1). */
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
			ch->sl.statem.msu_fisu_accepted = 1;
		else
			ch->sl.statem.msu_fisu_accepted = 0;
		ch->sl.statem.abnormal_bsnr = 0;
		ch->sl.statem.abnormal_fibr = 0;	/* Basic only (note 1). */
		ch->sl.statem.congestion_discard = 0;
		ch->sl.statem.congestion_accept = 0;
		sl_daedr_start(ch);
		ch->sl.statem.rc_state = SL_STATE_IN_SERVICE;
		return;
		/* 
		 *   Note 1 - Although rtr and abnormal_fibr are only applicable to the Basic procedure (and
		 *   not PCR), these state machine variables are never examined by PCR routines, so PCR and
		 *   basic can share the same start procedures.
		 */
	}
}

noinline fastcall void
sl_rc_reject_msu_fisu(struct ch *ch)
{
	ch->sl.statem.msu_fisu_accepted = 0;
}

noinline fastcall void
sl_rc_accept_msu_fisu(struct ch *ch)
{
	ch->sl.statem.msu_fisu_accepted = 1;
}

noinline fastcall __unlikely void
sl_rc_retrieve_fsnx(struct ch *ch)
{
	sl_txc_fsnx_value(ch);	/* error in 93 spec */
	ch->sl.statem.congestion_discard = 0;
	ch->sl.statem.congestion_accept = 0;
	sl_cc_normal(ch);
	ch->sl.statem.rtr = 0;	/* basic only */
}

noinline fastcall __unlikely void
sl_rc_align_fsnx(struct ch *ch)
{
	sl_txc_fsnx_value(ch);
}

noinline fastcall __unlikely void
sl_rc_clear_rb(struct ch *ch, queue_t *q, mblk_t *mp)
{
	bufq_purge(&ch->sl.rb);
	sl_flush_rq(ch);
	ch->sl.statem.Cr = 0;
	sl_rb_cleared_ind(ch, q, mp);
}

noinline fastcall __unlikely void
sl_rc_retrieve_bsnt(struct ch *ch, queue_t *q, mblk_t *mp)
{
	ch->sl.statem.rx.T.bsn = (ch->sl.statem.rx.X.fsn - 1) & 0x7F;
	sl_bsnt_ind(ch, q, mp);
}

noinline fastcall void
sl_cc_busy(struct ch *ch)
{
	if (ch->sl.statem.cc_state == SL_STATE_NORMAL) {
		sl_txc_send_sib(ch);
		xp_timer_start(ch, t5);
		ch->sl.statem.cc_state = SL_STATE_BUSY;
	}
}

noinline fastcall __unlikely void
sl_rc_congestion_discard(struct ch *ch)
{
	ch->sl.statem.congestion_discard = 1;
	sl_cc_busy(ch);
}

noinline fastcall __unlikely void
sl_rc_congestion_accept(struct ch *ch)
{
	ch->sl.statem.congestion_accept = 1;
	sl_cc_busy(ch);
}

noinline fastcall __unlikely void
sl_rc_no_congestion(struct ch *ch)
{
	ch->sl.statem.congestion_discard = 0;
	ch->sl.statem.congestion_accept = 0;
	sl_cc_normal(ch);
	sl_txc_fsnx_value(ch);
	if (ch->sl.statem.rtr == 1) {	/* rtr never set for PCR */
		sl_txc_nack_to_be_sent(ch);
		ch->sl.statem.rx.X.fib = ch->sl.statem.rx.X.fib ? 0 : ch->sl.statem.ib_mask;
	}
}

noinline fastcall __unlikely void
sl_lsc_congestion_discard(struct ch *ch)
{
	sl_rc_congestion_discard(ch);
	ch->sl.statem.l3_congestion_detect = 1;
}

noinline fastcall __unlikely void
sl_lsc_congestion_accept(struct ch *ch)
{
	sl_rc_congestion_accept(ch);
	ch->sl.statem.l3_congestion_detect = 1;
}

noinline fastcall __unlikely void
sl_lsc_no_congestion(struct ch *ch)
{
	sl_rc_no_congestion(ch);
	ch->sl.statem.l3_congestion_detect = 0;
}

noinline fastcall void
sl_lsc_sio(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
	case SL_STATE_INITIAL_ALIGNMENT:
		break;
	default:
		xp_timer_stop(ch, t1);	/* ok if not running */
		ch->sl.statem.failure_reason = SL_FAIL_RECEIVED_SIO;
		sl_out_of_service_ind(ch);
		sl_rc_stop(ch);
		sl_suerm_stop(ch);
		sl_poc_stop(ch);	/* ok if ANSI */
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		/* FIXME: reinspect */
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.remote_processor_outage = 0;	/* ok if ITUT */
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
		break;
	}
}

noinline fastcall void
sl_lsc_alignment_not_possible(struct ch *ch)
{
	ch->sl.statem.failure_reason = SL_FAIL_ALIGNMENT_NOT_POSSIBLE;
	sl_out_of_service_ind(ch);
	sl_rc_stop(ch);
	sl_txc_send_sios(ch);
	ch->sl.statem.local_processor_outage = 0;
	ch->sl.statem.emergency = 0;
	ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
	ch->sl.stats.sl_fail_align_or_proving++;
}

noinline fastcall void
sl_iac_sio(struct ch *ch)
{
	switch (ch->sl.statem.iac_state) {
	case SL_STATE_NOT_ALIGNED:
		xp_timer_stop(ch, t2);
		if (ch->sl.statem.emergency) {
			ch->sl.statem.t4v = ch->sl.config.t4e;
			printd(("Sending SIE at %lu\n", jiffies));
			sl_txc_send_sie(ch);
		} else {
			ch->sl.statem.t4v = ch->sl.config.t4n;
			sl_txc_send_sin(ch);
		}
		xp_timer_start(ch, t3);
		ch->sl.statem.iac_state = SL_STATE_ALIGNED;
		break;
	case SL_STATE_PROVING:
		xp_timer_stop(ch, t4);
		sl_aerm_stop(ch);
		xp_timer_start(ch, t3);
		ch->sl.statem.iac_state = SL_STATE_ALIGNED;
		break;
	}
}

noinline fastcall void
sl_iac_sios(struct ch *ch)
{
	switch (ch->sl.statem.iac_state) {
	case SL_STATE_ALIGNED:
	case SL_STATE_PROVING:
		xp_timer_stop(ch, t4);	/* ok if not running */
		sl_lsc_alignment_not_possible(ch);
		sl_aerm_stop(ch);	/* ok if not running */
		xp_timer_stop(ch, t3);	/* ok if not running */
		ch->sl.statem.emergency = 0;
		ch->sl.statem.iac_state = SL_STATE_IDLE;
		break;
	}
}

noinline fastcall void
sl_lsc_sios(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_ALIGNED_READY:
	case SL_STATE_ALIGNED_NOT_READY:
		xp_timer_stop(ch, t1);	/* ok to stop if not running */
	case SL_STATE_IN_SERVICE:
	case SL_STATE_PROCESSOR_OUTAGE:
		ch->sl.statem.failure_reason = SL_FAIL_RECEIVED_SIOS;
		sl_out_of_service_ind(ch);
		sl_suerm_stop(ch);	/* ok to stop if not running */
		sl_rc_stop(ch);
		sl_poc_stop(ch);	/* ok if ANSI */
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.remote_processor_outage = 0;	/* ok if ITU */
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
		break;
	}
}

noinline fastcall __unlikely void
sl_lsc_no_processor_outage(struct ch *ch)
{
	if (ch->sl.statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE) {
		ch->sl.statem.processor_outage = 0;
		if (!ch->sl.statem.l3_indication_received) {
			return;
		}
		ch->sl.statem.l3_indication_received = 0;
		sl_txc_send_msu(ch);
		ch->sl.statem.local_processor_outage = 0;
		sl_rc_accept_msu_fisu(ch);
		ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
	}
}

noinline fastcall __unlikely void
sl_poc_remote_processor_recovered(struct ch *ch)
{
	switch (ch->sl.statem.poc_state) {
	case SL_STATE_REMOTE_PROCESSOR_OUTAGE:
		/* Indication moved from caller to remove spurious remote processor recovered indications. */
		sl_remote_processor_recovered_ind(ch);
		sl_lsc_no_processor_outage(ch);
		ch->sl.statem.poc_state = SL_STATE_IDLE;
		break;
	case SL_STATE_BOTH_PROCESSORS_OUT:
		/* Indication moved from caller to remove spurious remote processor recovered indications. */
		sl_remote_processor_recovered_ind(ch);
		ch->sl.statem.poc_state = SL_STATE_LOCAL_PROCESSOR_OUTAGE;
		break;
	}
}

noinline fastcall void
sl_lsc_fisu_msu_received(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_ALIGNED_READY:
		ch->sl.statem.failure_reason = 0;
		sl_in_service_ind(ch);
		if (ch->option.pvar == SS7_PVAR_ITUT_93)
			sl_rc_accept_msu_fisu(ch);	/* unnecessary */
		xp_timer_stop(ch, t1);
		sl_txc_send_msu(ch);
		ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
		break;
	case SL_STATE_ALIGNED_NOT_READY:
		ch->sl.statem.failure_reason = 0;
		sl_in_service_ind(ch);
		xp_timer_stop(ch, t1);
		ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		break;
	case SL_STATE_PROCESSOR_OUTAGE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			/* 
			 *  A deviation from the SDLs has been placed here to limit the number of remote
			 *  processor recovered indications which are delivered to L3.  One indication is
			 *  sufficient.
			 */
			if (ch->sl.statem.remote_processor_outage) {
				sl_remote_processor_recovered_ind(ch);
				ch->sl.statem.remote_processor_outage = 0;
			}
		} else {
			sl_poc_remote_processor_recovered(ch);
		}
		break;
	}
}

noinline fastcall __unlikely void
sl_poc_remote_processor_outage(struct ch *ch)
{
	switch (ch->sl.statem.poc_state) {
	case SL_STATE_IDLE:
		/* Moved here from caller to limit the number of remote processor outage indications
		   delivered to L3. */
		sl_remote_processor_outage_ind(ch, NULL, NULL);
		ch->sl.statem.poc_state = SL_STATE_REMOTE_PROCESSOR_OUTAGE;
		break;
	case SL_STATE_LOCAL_PROCESSOR_OUTAGE:
		/* Moved here from caller to limit the number of remote processor outage indications
		   delivered to L3. */
		sl_remote_processor_outage_ind(ch, NULL, NULL);
		ch->sl.statem.poc_state = SL_STATE_BOTH_PROCESSORS_OUT;
		break;
	}
}

noinline fastcall __unlikely void
sl_lsc_sib(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_IN_SERVICE:
	case SL_STATE_PROCESSOR_OUTAGE:
		sl_txc_sib_received(ch);
		break;
	}
}

noinline fastcall __unlikely void
sl_lsc_sipo(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_ALIGNED_READY:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			xp_timer_stop(ch, t1);
			sl_remote_processor_outage_ind(ch, NULL, NULL);
			ch->sl.statem.remote_processor_outage = 1;
			ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		} else {
			xp_timer_stop(ch, t1);
			sl_poc_remote_processor_outage(ch);
			ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		}
		break;
	case SL_STATE_ALIGNED_NOT_READY:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			sl_remote_processor_outage_ind(ch, NULL, NULL);
			ch->sl.statem.remote_processor_outage = 1;
			xp_timer_stop(ch, t1);
			ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		} else {
			sl_poc_remote_processor_outage(ch);
			xp_timer_stop(ch, t1);
			ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		}
		break;
	case SL_STATE_IN_SERVICE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			sl_txc_send_fisu(ch);
			sl_remote_processor_outage_ind(ch, NULL, NULL);
			ch->sl.statem.remote_processor_outage = 1;
			sl_rc_align_fsnx(ch);
			sl_cc_stop(ch);
			ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		} else {
			sl_txc_send_fisu(ch);
			sl_poc_remote_processor_outage(ch);
			ch->sl.statem.processor_outage = 1;	/* remote? */
			ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		}
		break;
	case SL_STATE_PROCESSOR_OUTAGE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
#if 0
			ch->sl.statem.remote_processor_outage = 1;
			sl_remote_processor_outage_ind(ch, NULL, NULL);
#else
			/* 
			 *  A deviation from the SDLs has been placed here to limit the number of
			 *  remote processor outage indications which are delivered to L3.  One
			 *  indication is sufficient.
			 */
			if (!ch->sl.statem.remote_processor_outage) {
				ch->sl.statem.remote_processor_outage = 1;
				sl_remote_processor_outage_ind(ch, NULL, NULL);
			}
#endif
		} else {
			sl_poc_remote_processor_outage(ch);
		}
		break;
	}
}

noinline fastcall __unlikely void
sl_poc_local_processor_outage(struct ch *ch)
{
	switch (ch->sl.statem.poc_state) {
	case SL_STATE_IDLE:
		ch->sl.statem.poc_state = SL_STATE_LOCAL_PROCESSOR_OUTAGE;
		return;
	case SL_STATE_REMOTE_PROCESSOR_OUTAGE:
		ch->sl.statem.poc_state = SL_STATE_BOTH_PROCESSORS_OUT;
		return;
	}
}

noinline fastcall __unlikely void
sl_eim_start(struct ch *ch)
{
	ch->sdt.statem.Ce = 0;
	ch->sdt.statem.interval_error = 0;
	ch->sdt.statem.su_received = 0;
	xp_timer_start(ch, t8);
	ch->sdt.statem.eim_state = SDT_STATE_MONITORING;
}

noinline fastcall __unlikely void
sl_suerm_start(struct ch *ch)
{
	if (ch->option.popt & SS7_POPT_HSL)
		sl_eim_start(ch);
	else {
		ch->sdt.statem.Cs = 0;
		ch->sdt.statem.Ns = 0;
		ch->sdt.statem.suerm_state = SDT_STATE_IN_SERVICE;
	}
}

noinline fastcall __unlikely void
sl_lsc_alignment_complete(struct ch *ch)
{
	if (ch->sl.statem.lsc_state == SL_STATE_INITIAL_ALIGNMENT) {
		sl_suerm_start(ch);
		xp_timer_start(ch, t1);
		if (ch->sl.statem.local_processor_outage) {
			if (((ch->option.pvar & SS7_PVAR_MASK) != SS7_PVAR_ANSI)
			    || ((ch->option.pvar & SS7_PVAR_YR) <= SS7_PVAR_88))
				sl_poc_local_processor_outage(ch);
			sl_txc_send_sipo(ch);
			if (ch->option.pvar != SS7_PVAR_ITUT_93)	/* possible error */
				sl_rc_reject_msu_fisu(ch);
			ch->sl.statem.lsc_state = SL_STATE_ALIGNED_NOT_READY;
		} else {
			sl_txc_send_msu(ch);	/* changed from send_fisu for Q.781 */
			sl_rc_accept_msu_fisu(ch);	/* error in ANSI spec? */
			ch->sl.statem.lsc_state = SL_STATE_ALIGNED_READY;
		}
	}
}

noinline fastcall void
sl_lsc_sin(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_IN_SERVICE:
		ch->sl.statem.failure_reason = SL_FAIL_RECEIVED_SIN;
		sl_out_of_service_ind(ch);
		sl_suerm_stop(ch);
		sl_rc_stop(ch);
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
		break;
	case SL_STATE_PROCESSOR_OUTAGE:
		ch->sl.statem.failure_reason = SL_FAIL_RECEIVED_SIN;
		sl_out_of_service_ind(ch);
		sl_suerm_stop(ch);
		sl_rc_stop(ch);
		sl_poc_stop(ch);	/* ok if not ITUT */
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.remote_processor_outage = 0;	/* ok if not ANSI */
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
		break;
	}
}

noinline fastcall __unlikely void
sl_aerm_set_ti_to_tie(struct ch *ch)
{
	if (ch->sdt.statem.aerm_state == SDT_STATE_IDLE)
		ch->sdt.statem.Ti = ch->sdt.config.Tie;
}

noinline fastcall __unlikely void
sl_aerm_start(struct ch *ch)
{
	ch->sdt.statem.Ca = 0;
	ch->sdt.statem.aborted_proving = 0;
	ch->sdt.statem.aerm_state = SDT_STATE_MONITORING;
}

noinline fastcall void
sl_iac_sin(struct ch *ch)
{
	switch (ch->sl.statem.iac_state) {
	case SL_STATE_NOT_ALIGNED:
		xp_timer_stop(ch, t2);
		if (ch->sl.statem.emergency) {
			ch->sl.statem.t4v = ch->sl.config.t4e;
			sl_txc_send_sie(ch);
		} else {
			ch->sl.statem.t4v = ch->sl.config.t4n;
			sl_txc_send_sin(ch);
		}
		xp_timer_start(ch, t3);
		ch->sl.statem.iac_state = SL_STATE_ALIGNED;
		return;
	case SL_STATE_ALIGNED:
		xp_timer_stop(ch, t3);
		if (ch->sl.statem.t4v == ch->sl.config.t4e)
			sl_aerm_set_ti_to_tie(ch);
		sl_aerm_start(ch);
		xp_timer_start(ch, t4);
		ch->sl.statem.further_proving = 0;
		ch->sl.statem.Cp = 0;
		ch->sl.statem.iac_state = SL_STATE_PROVING;
		return;
	}
}

noinline fastcall void
sl_lsc_sie(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_IN_SERVICE:
		ch->sl.statem.failure_reason = SL_FAIL_RECEIVED_SIE;
		sl_out_of_service_ind(ch);
		sl_suerm_stop(ch);
		sl_rc_stop(ch);
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
		break;
	case SL_STATE_PROCESSOR_OUTAGE:
		ch->sl.statem.failure_reason = SL_FAIL_RECEIVED_SIE;
		sl_out_of_service_ind(ch);
		sl_suerm_stop(ch);
		sl_rc_stop(ch);
		sl_poc_stop(ch);	/* ok if not ITUT */
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.remote_processor_outage = 0;	/* ok if not ANSI */
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
		break;
	}
}

noinline fastcall void
sl_iac_sie(struct ch *ch)
{
	switch (ch->sl.statem.iac_state) {
	case SL_STATE_NOT_ALIGNED:
		xp_timer_stop(ch, t2);
		if (ch->sl.statem.emergency) {
			ch->sl.statem.t4v = ch->sl.config.t4e;
			sl_txc_send_sie(ch);
		} else {
			ch->sl.statem.t4v = ch->sl.config.t4e;	/* yes e */
			sl_txc_send_sin(ch);
		}
		xp_timer_start(ch, t3);
		ch->sl.statem.iac_state = SL_STATE_ALIGNED;
		return;
	case SL_STATE_ALIGNED:
		printd(("Receiving SIE at %lu\n", jiffies));
		ch->sl.statem.t4v = ch->sl.config.t4e;
		xp_timer_stop(ch, t3);
		sl_aerm_set_ti_to_tie(ch);
		sl_aerm_start(ch);
		xp_timer_start(ch, t4);
		ch->sl.statem.further_proving = 0;
		ch->sl.statem.Cp = 0;
		ch->sl.statem.iac_state = SL_STATE_PROVING;
		return;
	case SL_STATE_PROVING:
		if (ch->sl.statem.t4v == ch->sl.config.t4e)
			return;
		xp_timer_stop(ch, t4);
		ch->sl.statem.t4v = ch->sl.config.t4e;
		sl_aerm_stop(ch);
		sl_aerm_set_ti_to_tie(ch);
		sl_aerm_start(ch);
		xp_timer_start(ch, t4);
		ch->sl.statem.further_proving = 0;
		return;
	}
}

/*
 *  --------------------------------------------------------------------------
 *
 *  These congestion functions are implementation dependent.  We should define a congestion onset
 *  level and set congestion accept at that point.  We should also define a second congestion onset
 *  level and set congestion discard at that point.  For STREAMS, the upstream congestion can be
 *  detected in two ways: 1) canputnext(): is the upstream module flow controlled; and, 2) canput():
 *  are we flow controlled.  If the upstream module is flow controlled, then we can accept MSUs and
 *  place them on our own read queue.  If we are flow contolled, then we have no choice but to
 *  discard the message.  In addition, and because upstream message processing times are likely more
 *  sensitive to the number of backlogged messages than they are to the number of backlogged message
 *  octets, we have some configurable thresholds of backlogging and keep track of backlogged
 *  messages.
 *
 *  --------------------------------------------------------------------------
 */

static inline fastcall __hot void
sl_rb_congestion_function(struct ch *ch)
{
	if (!ch->sl.statem.l3_congestion_detect) {
		struct xp *xp = ch->xp;

		if (ch->sl.statem.l2_congestion_detect) {
			if (ch->sl.statem.Cr <= ch->sl.config.rb_abate && (!xp || canputnext(xp->rq))) {
				sl_rc_no_congestion(ch);
				ch->sl.statem.l2_congestion_detect = 0;
			}
		} else {
			if (ch->sl.statem.Cr >= ch->sl.config.rb_discard || (xp && !canput(xp->rq))) {
				sl_rc_congestion_discard(ch);
				ch->sl.statem.l2_congestion_detect = 1;
			} else if (ch->sl.statem.Cr >= ch->sl.config.rb_accept || (xp && !canputnext(xp->rq))) {
				sl_rc_congestion_accept(ch);
				ch->sl.statem.l2_congestion_detect = 1;
			}
		}
	}
}

static inline fastcall __hot_in void
sl_rc_signal_unit(struct ch *ch, mblk_t *mp)
{
	int pcr = ch->option.popt & SS7_POPT_PCR;

	if (ch->sl.statem.rc_state != SL_STATE_IN_SERVICE) {
		freemsg(mp);
		return;
	}
	/* Note: the driver must check that the length of the frame is within appropriate bounds as specified 
	   by the DAEDR in Q.703.  If the length of the frame is incorrect, it should indicate
	   daedr_error_frame rather than daedr_received_frame.  */
	if (ch->option.popt & SS7_POPT_XSN) {
		ch->sl.statem.rx.R.bsn = ntohs(*((sl_ushort *) mp->b_rptr)) & 0x0fff;
		ch->sl.statem.rx.R.bib = ntohs(*(sl_ushort *) mp->b_rptr++) & 0x8000;
		ch->sl.statem.rx.R.fsn = ntohs(*((sl_ushort *) mp->b_rptr)) & 0x0fff;
		ch->sl.statem.rx.R.fib = ntohs(*(sl_ushort *) mp->b_rptr++) & 0x8000;
		ch->sl.statem.rx.len = ntohs(*(sl_ushort *) mp->b_rptr++) & 0x01ff;
	} else {
		ch->sl.statem.rx.R.bsn = *mp->b_rptr & 0x7f;
		ch->sl.statem.rx.R.bib = *mp->b_rptr++ & 0x80;
		ch->sl.statem.rx.R.fsn = *mp->b_rptr & 0x7f;
		ch->sl.statem.rx.R.fib = *mp->b_rptr++ & 0x80;
		ch->sl.statem.rx.len = *mp->b_rptr++ & 0x3f;
	}
	if (ch->sl.statem.rx.len == 1) {
		ch->sl.statem.rx.sio = *mp->b_rptr++ & 0x07;
	}
	if (ch->sl.statem.rx.len == 2) {
		ch->sl.statem.rx.sio = *mp->b_rptr++ & 0x07;
		ch->sl.statem.rx.sio = *mp->b_rptr++ & 0x07;
	}
#if 0
	ptrace(("rx: bsn=%x, bib=%x, fsn=%x, fib=%x, len=%d, sio=%d\n", ch->sl.statem.rx.R.bsn,
		ch->sl.statem.rx.R.bib, ch->sl.statem.rx.R.fsn, ch->sl.statem.rx.R.fib, ch->sl.statem.rx.len,
		ch->sl.statem.rx.sio));
#endif
	if (((ch->sl.statem.rx.len) == 1) || ((ch->sl.statem.rx.len) == 2)) {
		switch (ch->sl.statem.rx.sio) {
		case LSSU_SIO:{
			sl_iac_sio(ch);
			sl_lsc_sio(ch);
			break;
		}
		case LSSU_SIN:{
			sl_iac_sin(ch);
			sl_lsc_sin(ch);
			break;
		}
		case LSSU_SIE:{
			sl_iac_sie(ch);
			sl_lsc_sie(ch);
			break;
		}
		case LSSU_SIOS:{
			sl_iac_sios(ch);
			sl_lsc_sios(ch);
			break;
		}
		case LSSU_SIPO:{
			sl_lsc_sipo(ch);
			break;
		}
		case LSSU_SIB:{
			sl_lsc_sib(ch);
			break;
		}
		}
		freemsg(mp);
		return;
	}
	if (SN_OUTSIDE
	    (((ch->sl.statem.rx.F.fsn - 1) & ch->sl.statem.sn_mask), ch->sl.statem.rx.R.bsn,
	     ch->sl.statem.rx.T.fsn)) {
		if (ch->sl.statem.abnormal_bsnr) {
			sl_lsc_link_failure(ch, SL_FAIL_ABNORMAL_BSNR);
			ch->sl.statem.rc_state = SL_STATE_IDLE;
			freemsg(mp);
			return;
		} else {
			ch->sl.statem.abnormal_bsnr = 1;
			ch->sl.statem.unb = 0;
			freemsg(mp);
			return;
		}
	}
	if (ch->sl.statem.abnormal_bsnr) {
		if (ch->sl.statem.unb == 1) {
			ch->sl.statem.abnormal_bsnr = 0;
		} else {
			ch->sl.statem.unb = 1;
			freemsg(mp);
			return;
		}
	}
	if (pcr) {
		sl_lsc_fisu_msu_received(ch);
		sl_txc_bsnr_and_bibr(ch);
		ch->sl.statem.rx.F.fsn = (ch->sl.statem.rx.R.bsn + 1) & ch->sl.statem.sn_mask;
		if (!ch->sl.statem.msu_fisu_accepted) {
			freemsg(mp);
			return;
		}
		sl_rb_congestion_function(ch);
		if (ch->sl.statem.congestion_discard) {
			sl_cc_busy(ch);
			freemsg(mp);
			return;
		}
		if ((ch->sl.statem.rx.R.fsn == ch->sl.statem.rx.X.fsn) && (ch->sl.statem.rx.len > 2)) {
			if (sl_pdu_ind(ch, mp) == 0) {
				ch->sl.statem.rx.X.fsn = (ch->sl.statem.rx.X.fsn + 1) & ch->sl.statem.sn_mask;
				ch->sl.statem.Cr++;
				if (ch->sl.statem.congestion_accept)
					sl_cc_busy(ch);
				else
					sl_txc_fsnx_value(ch);
			} else {
				/* same as congestion discard */
				sl_cc_busy(ch);
				freemsg(mp);
			}
			return;
		} else {
			freemsg(mp);
			return;
		}
		return;
	}
	if (ch->sl.statem.rx.R.fib == ch->sl.statem.rx.X.fib) {
		if (ch->sl.statem.abnormal_fibr) {
			if (ch->sl.statem.unf == 1) {
				ch->sl.statem.abnormal_fibr = 0;
			} else {
				ch->sl.statem.unf = 1;
				freemsg(mp);
				return;
			}
		}
		sl_lsc_fisu_msu_received(ch);
		sl_txc_bsnr_and_bibr(ch);
		ch->sl.statem.rx.F.fsn = (ch->sl.statem.rx.R.bsn + 1) & ch->sl.statem.sn_mask;
		if (!ch->sl.statem.msu_fisu_accepted) {
			freemsg(mp);
			return;
		}
		sl_rb_congestion_function(ch);
		if (ch->sl.statem.congestion_discard) {
			ch->sl.statem.rtr = 1;
			freemsg(mp);
			sl_cc_busy(ch);
			return;
		}
		if ((ch->sl.statem.rx.R.fsn == ch->sl.statem.rx.X.fsn) && (ch->sl.statem.rx.len > 2)) {
			if (sl_pdu_ind(ch, mp) == 0) {
				ch->sl.statem.rx.X.fsn = (ch->sl.statem.rx.X.fsn + 1) & ch->sl.statem.sn_mask;
				ch->sl.statem.rtr = 0;
				ch->sl.statem.Cr++;
				if (ch->sl.statem.congestion_accept)
					sl_cc_busy(ch);
				else
					sl_txc_fsnx_value(ch);
			} else {
				/* same as congestion discard */
				ch->sl.statem.rtr = 1;
				freemsg(mp);
				sl_cc_busy(ch);
			}
			return;
		}
		if ((ch->sl.statem.rx.R.fsn == ((ch->sl.statem.rx.X.fsn - 1) & ch->sl.statem.sn_mask))) {
			freemsg(mp);
			return;
		} else {
			if (ch->sl.statem.congestion_accept) {
				ch->sl.statem.rtr = 1;
				sl_cc_busy(ch);	/* not required? */
				freemsg(mp);
				return;
			} else {
				sl_txc_nack_to_be_sent(ch);
				ch->sl.statem.rtr = 1;
				ch->sl.statem.rx.X.fib = ch->sl.statem.rx.X.fib ? 0 : ch->sl.statem.ib_mask;
				freemsg(mp);
				return;
			}
		}
	} else {
		if (ch->sl.statem.abnormal_fibr) {
			sl_lsc_link_failure(ch, SL_FAIL_ABNORMAL_FIBR);
			freemsg(mp);
			return;
		}
		if (ch->sl.statem.rtr == 1) {
			ch->sl.stats.sl_nacks_received++;
			sl_txc_bsnr_and_bibr(ch);
			ch->sl.statem.rx.F.fsn = (ch->sl.statem.rx.R.bsn + 1) & ch->sl.statem.sn_mask;
			freemsg(mp);
			return;
		}
		ch->sl.statem.abnormal_fibr = 1;
		ch->sl.statem.unf = 0;
		freemsg(mp);
		return;
	}
}

noinline fastcall __unlikely void
sl_lsc_stop(struct ch *ch)
{
	if (ch->sl.statem.lsc_state != SL_STATE_OUT_OF_SERVICE) {
		sl_iac_stop(ch);	/* ok if not running */
		xp_timer_stop(ch, t1);	/* ok if not running */
		sl_rc_stop(ch);
		sl_suerm_stop(ch);	/* ok if not running */
		sl_poc_stop(ch);	/* ok if not running or not ITUT */
		sl_txc_send_sios(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.remote_processor_outage = 0;	/* ok of not ANSI */
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
	}
}

noinline fastcall __unlikely void
sl_lsc_clear_rtb(struct ch *ch, queue_t *q, mblk_t *mp)
{
	if (ch->sl.statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE) {
		ch->sl.statem.local_processor_outage = 0;
		sl_txc_send_fisu(ch);
		sl_txc_clear_rtb(ch, q, mp);
	} else
		freemsg(mp);
}

noinline fastcall void
sl_iac_correct_su(struct ch *ch)
{
	if (ch->sl.statem.iac_state == SL_STATE_PROVING) {
		if (ch->sl.statem.further_proving) {
			xp_timer_stop(ch, t4);
			sl_aerm_start(ch);
			xp_timer_start(ch, t4);
			ch->sl.statem.further_proving = 0;
		}
	}
}

noinline fastcall __unlikely void
sl_iac_abort_proving(struct ch *ch)
{
	if (ch->sl.statem.iac_state == SL_STATE_PROVING) {
		ch->sl.statem.Cp++;
		if (ch->sl.statem.Cp == ch->sl.config.M) {
			sl_lsc_alignment_not_possible(ch);
			xp_timer_stop(ch, t4);
			sl_aerm_stop(ch);
			ch->sl.statem.emergency = 0;
			ch->sl.statem.iac_state = SL_STATE_IDLE;
			return;
		}
		ch->sl.statem.further_proving = 1;
	}
}

#define sl_lsc_flush_buffers sl_lsc_clear_buffers
noinline fastcall __unlikely void
sl_lsc_clear_buffers(struct ch *ch, queue_t *q, mblk_t *mp)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			sl_rtb_cleared_ind(ch, q, mp);
			ch->sl.statem.local_processor_outage = 0;	/* ??? */
		}
		break;
	case SL_STATE_INITIAL_ALIGNMENT:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			sl_rtb_cleared_ind(ch, q, mp);
			ch->sl.statem.local_processor_outage = 0;
		}
		break;
	case SL_STATE_ALIGNED_NOT_READY:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			sl_rtb_cleared_ind(ch, q, mp);
			ch->sl.statem.local_processor_outage = 0;
			sl_txc_send_fisu(ch);
			ch->sl.statem.lsc_state = SL_STATE_ALIGNED_READY;
		}
		break;
	case SL_STATE_PROCESSOR_OUTAGE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			ch->sl.statem.local_processor_outage = 0;
			sl_rc_clear_rb(ch, q, mp);
			sl_rc_accept_msu_fisu(ch);
			sl_txc_send_fisu(ch);
			sl_txc_clear_tb(ch, q, NULL);
			sl_txc_clear_rtb(ch, q, NULL);
		} else {
			sl_txc_flush_buffers(ch);
			ch->sl.statem.l3_indication_received = 1;
			if (ch->sl.statem.processor_outage)
				return;
			ch->sl.statem.l3_indication_received = 0;
			sl_txc_send_msu(ch);
			ch->sl.statem.local_processor_outage = 0;
			sl_rc_accept_msu_fisu(ch);
			ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
		}
		break;
	default:
		freemsg(mp);
		break;
	}
}

noinline fastcall __unlikely void
sl_lsc_continue(struct ch *ch)
{
	if (ch->sl.statem.lsc_state == SL_STATE_PROCESSOR_OUTAGE) {
		ch->sl.statem.l3_indication_received = 1;
		if (ch->sl.statem.processor_outage) {
			return;
		}
		ch->sl.statem.l3_indication_received = 0;
		sl_txc_send_msu(ch);
		ch->sl.statem.local_processor_outage = 0;
		sl_rc_accept_msu_fisu(ch);
		ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
	}
}

noinline fastcall __unlikely void
sl_poc_local_processor_recovered(struct ch *ch)
{
	switch (ch->sl.statem.poc_state) {
	case SL_STATE_LOCAL_PROCESSOR_OUTAGE:
		sl_lsc_no_processor_outage(ch);
		ch->sl.statem.poc_state = SL_STATE_IDLE;
		return;
	case SL_STATE_BOTH_PROCESSORS_OUT:
		ch->sl.statem.poc_state = SL_STATE_REMOTE_PROCESSOR_OUTAGE;
		return;
	}
}

#define sl_lsc_resume sl_lsc_local_processor_recovered
noinline fastcall __unlikely int
sl_lsc_local_processor_recovered(struct ch *ch, queue_t *q, mblk_t *mp)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
		ch->sl.statem.local_processor_outage = 0;
		break;
	case SL_STATE_INITIAL_ALIGNMENT:
		ch->sl.statem.local_processor_outage = 0;
		break;
	case SL_STATE_ALIGNED_READY:
		break;
	case SL_STATE_ALIGNED_NOT_READY:
		if (((ch->option.pvar & SS7_PVAR_MASK) != SS7_PVAR_ANSI)
		    || ((ch->option.pvar & SS7_PVAR_YR) <= SS7_PVAR_88))
			sl_poc_local_processor_recovered(ch);
		ch->sl.statem.local_processor_outage = 0;
		sl_txc_send_fisu(ch);
		if (ch->option.pvar == SS7_PVAR_ITUT_96)
			sl_rc_accept_msu_fisu(ch);
		ch->sl.statem.lsc_state = SL_STATE_ALIGNED_READY;
		break;
	case SL_STATE_PROCESSOR_OUTAGE:
		if (((ch->option.pvar & SS7_PVAR_MASK) != SS7_PVAR_ANSI)
		    || ((ch->option.pvar & SS7_PVAR_YR) <= SS7_PVAR_88)) {
			sl_poc_local_processor_recovered(ch);
			sl_rc_retrieve_fsnx(ch);
			if (ch->sl.statem.poc_state != SL_STATE_IDLE)
				sl_txc_send_fisu(ch);	/* note 3: in fisu BSN <= FSNX-1 */
			// ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
		} else {
			ch->sl.statem.local_processor_outage = 0;
			sl_rc_accept_msu_fisu(ch);
			if (ch->sl.statem.remote_processor_outage) {
				sl_txc_send_fisu(ch);
				return sl_remote_processor_outage_ind(ch, q, mp);
			}
			sl_txc_send_msu(ch);
			ch->sl.statem.lsc_state = SL_STATE_IN_SERVICE;
		}
		break;
	}
	freemsg(mp);
	return (0);
}

#define sl_lsc_level_3_failure sl_lsc_local_processor_outage
noinline fastcall __unlikely void
sl_lsc_local_processor_outage(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
	case SL_STATE_INITIAL_ALIGNMENT:
		ch->sl.statem.local_processor_outage = 1;
		return;
	case SL_STATE_ALIGNED_READY:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
			ch->sl.statem.local_processor_outage = 1;
		else
			sl_poc_local_processor_outage(ch);
		sl_txc_send_sipo(ch);
		if (ch->option.pvar != SS7_PVAR_ITUT_93)	/* possible error 93 specs */
			sl_rc_reject_msu_fisu(ch);
		ch->sl.statem.lsc_state = SL_STATE_ALIGNED_NOT_READY;
		return;
	case SL_STATE_IN_SERVICE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			ch->sl.statem.local_processor_outage = 1;
		} else {
			sl_poc_local_processor_outage(ch);
			ch->sl.statem.processor_outage = 1;
		}
		sl_txc_send_sipo(ch);
		sl_rc_reject_msu_fisu(ch);
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
			sl_rc_align_fsnx(ch);
			sl_cc_stop(ch);
		}
		ch->sl.statem.lsc_state = SL_STATE_PROCESSOR_OUTAGE;
		return;
	case SL_STATE_PROCESSOR_OUTAGE:
		if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
		    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88))
			ch->sl.statem.local_processor_outage = 1;
		else
			sl_poc_local_processor_outage(ch);
		sl_txc_send_sipo(ch);
		return;
	}
}

noinline fastcall __unlikely void
sl_iac_emergency(struct ch *ch)
{
	switch (ch->sl.statem.iac_state) {
	case SL_STATE_PROVING:
		sl_txc_send_sie(ch);
		xp_timer_stop(ch, t4);
		ch->sl.statem.t4v = ch->sl.config.t4e;
		sl_aerm_stop(ch);
		sl_aerm_set_ti_to_tie(ch);
		sl_aerm_start(ch);
		xp_timer_start(ch, t4);
		ch->sl.statem.further_proving = 0;
		return;
	case SL_STATE_ALIGNED:
		sl_txc_send_sie(ch);
		ch->sl.statem.t4v = ch->sl.config.t4e;
		return;
	case SL_STATE_IDLE:
	case SL_STATE_NOT_ALIGNED:
		ch->sl.statem.emergency = 1;
	}
}

noinline fastcall __unlikely void
sl_lsc_emergency(struct ch *ch)
{
	ch->sl.statem.emergency = 1;
	sl_iac_emergency(ch);	/* added to pass Q.781/Test 1.20 */
}

noinline fastcall __unlikely void
sl_lsc_emergency_ceases(struct ch *ch)
{
	ch->sl.statem.emergency = 0;
}

noinline fastcall __unlikely void
sl_iac_start(struct ch *ch)
{
	if (ch->sl.statem.iac_state == SL_STATE_IDLE) {
		sl_txc_send_sio(ch);
		xp_timer_start(ch, t2);
		ch->sl.statem.iac_state = SL_STATE_NOT_ALIGNED;
	}
}

noinline fastcall __unlikely void
sl_daedt_start(struct ch *ch)
{
	ch->sdt.statem.daedt_state = SDT_STATE_IN_SERVICE;
	ch->sdl.statem.tx_state = SDL_STATE_IN_SERVICE;
	ch->sdl.config.ifflags |= (SDL_IF_UP | SDL_IF_TX_RUNNING);
}

noinline fastcall __unlikely void
sl_txc_start(struct ch *ch)
{
	ch->sl.statem.forced_retransmission = 0;	/* ok if basic */
	ch->sl.statem.sib_received = 0;
	ch->sl.statem.Ct = 0;
	ch->sl.statem.rtb_full = 0;
	ch->sl.statem.clear_rtb = 0;	/* ok if ITU */
	if (((ch->option.pvar & SS7_PVAR_MASK) == SS7_PVAR_ANSI)
	    && ((ch->option.pvar & SS7_PVAR_YR) > SS7_PVAR_88)) {
		ch->sl.statem.tx.sio = LSSU_SIOS;
		ch->sl.statem.lssu_available = 1;
	}
	ch->sl.statem.msu_inhibited = 0;
	if (ch->option.popt & SS7_POPT_XSN) {
		ch->sl.statem.sn_mask = 0x7fff;
		ch->sl.statem.ib_mask = 0x8000;
	} else {
		ch->sl.statem.sn_mask = 0x7f;
		ch->sl.statem.ib_mask = 0x80;
	}
	ch->sl.statem.tx.L.fsn = ch->sl.statem.tx.N.fsn = ch->sl.statem.sn_mask;
	ch->sl.statem.tx.X.fsn = 0;
	ch->sl.statem.tx.N.fib = ch->sl.statem.tx.N.bib = ch->sl.statem.ib_mask;
	ch->sl.statem.tx.F.fsn = 0;
	ch->sl.statem.Cm = 0;
	ch->sl.statem.Z = 0;
	ch->sl.statem.z_ptr = NULL;	/* ok if basic */
	if (ch->sl.statem.txc_state == SL_STATE_IDLE) {
		if (((ch->option.pvar & SS7_PVAR_MASK) != SS7_PVAR_ANSI)
		    || ((ch->option.pvar & SS7_PVAR_YR) <= SS7_PVAR_88))
			ch->sl.statem.lssu_available = 0;
		sl_daedt_start(ch);
	}
	ch->sl.statem.txc_state = SL_STATE_IN_SERVICE;
	return;
}

noinline fastcall __unlikely void
sl_lsc_start(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
		sl_rc_start(ch);
		sl_txc_start(ch);	/* Note 2 */
		if (ch->sl.statem.emergency)
			sl_iac_emergency(ch);
		sl_iac_start(ch);
		ch->sl.statem.lsc_state = SL_STATE_INITIAL_ALIGNMENT;
	}
}

/*
 *  Note 2: There is a difference here between ANSI_92 and ITUT_93/96 in that the transmitters in
 *  the ANSI_92 case may transmit one or two SIOSs before transmitting the first SIO of the initial
 *  alignment procedure.  ITUT will continue idling FISU or LSSU as before the start, then transmit
 *  the first SIO.  These are equivalent.  Because the LSC is in the OUT OF SERVICE state, the
 *  transmitters should be idling SIOS anyway.
 */

noinline fastcall __unlikely void
sl_lsc_retrieve_bsnt(struct ch *ch, queue_t *q, mblk_t *mp)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
	case SL_STATE_PROCESSOR_OUTAGE:
		sl_rc_retrieve_bsnt(ch, q, mp);
		break;
	default:
		freemsg(mp);
		break;
	}
}

noinline fastcall __unlikely int
sl_lsc_retrieval_request_and_fsnc(struct ch *ch, queue_t *q, mblk_t *mp, sl_ulong fsnc)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_OUT_OF_SERVICE:
	case SL_STATE_PROCESSOR_OUTAGE:
		return sl_txc_retrieval_request_and_fsnc(ch, q, mp, fsnc);
	default:
		freemsg(mp);
		return (0);
	}
}

noinline fastcall __unlikely void
sl_aerm_set_ti_to_tin(struct ch *ch)
{
	if (ch->sdt.statem.aerm_state == SDT_STATE_IDLE)
		ch->sdt.statem.Ti = ch->sdt.config.Tin;
}

/** sl_lsc_power_on: - power on the link
  * @ch: channel structure (locked)
  *
  * This power-on sequence should only be performed once, regardless of how many times the device
  * driver is opened or closed.  This initializes the transmitters to send SIOS and should never be
  * changed hence.
  *
  * Note 3: There is a difference here between ANSI_92 and ITUT_93/96 in that the transmitters in
  * the ITUT case may transmit one or two FISUs before transmitting SIOS on initial power-up.  ANSI
  * will send SIOS on power-up.  ANSI is the correct procedure as transmitters should always idle
  * SIOS on power-up.
  */
noinline fastcall __unlikely void
sl_lsc_power_on(struct ch *ch)
{
	switch (ch->sl.statem.lsc_state) {
	case SL_STATE_POWER_OFF:
		sl_txc_start(ch);	/* Note 3 */
		sl_txc_send_sios(ch);	/* not necessary for ANSI */
		sl_aerm_set_ti_to_tin(ch);
		ch->sl.statem.local_processor_outage = 0;
		ch->sl.statem.emergency = 0;
		ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
	}
}

/** sl_check_congedstion: - transmit congestion algorithm
  * @ch: channel structure (locked)
  * @q: active queue (write queue or NULL)
  * @mp: message to reuse (or NULL)
  *
  * The transmit congestion algorithm is an implementation dependent algorithm but is suggested as
  * being based on TB and/or RTB buffer occupancy.  With STREAMS we can use octet count buffer
  * occupancy over message count occupancy, because congestion in transmission is more related to
  * octet count (because it determines transmission latency).
  *
  * We check the total buffer occupancy and apply the necessary congestion control signal as per
  * configured abatement, onset and discard thresholds.
  */
STATIC fastcall __hot_write int
sl_check_congestion(struct ch *ch, queue_t *q, mblk_t *mp)
{
	unsigned int occupancy = sl_count_rq(ch) + ch->sl.tb.q_count + ch->sl.rtb.q_count;
	int old_cong_status = ch->sl.statem.cong_status;
	int old_disc_status = ch->sl.statem.disc_status;
	int multi = ch->option.popt & SS7_POPT_MPLEV;

	switch (ch->sl.statem.cong_status) {
	case 0:
		if (occupancy < ch->sl.config.tb_onset_1)
			break;
		ch->sl.statem.cong_status = 1;
		if (occupancy < ch->sl.config.tb_discd_1)
			break;
		ch->sl.statem.disc_status = 1;
		if (occupancy < ch->sl.config.tb_onset_2 || !multi)
			break;
		ch->sl.statem.cong_status = 2;
		if (occupancy < ch->sl.config.tb_discd_2)
			break;
		ch->sl.statem.disc_status = 2;
		if (occupancy < ch->sl.config.tb_onset_3)
			break;
		ch->sl.statem.cong_status = 3;
		if (occupancy < ch->sl.config.tb_discd_3)
			break;
		ch->sl.statem.disc_status = 3;
		break;
	case 1:
		if (occupancy < ch->sl.config.tb_abate_1) {
			ch->sl.statem.cong_status = 0;
			ch->sl.statem.disc_status = 0;
			break;
		}
		if (occupancy < ch->sl.config.tb_onset_2 || !multi)
			break;
		ch->sl.statem.cong_status = 2;
		if (occupancy < ch->sl.config.tb_discd_2)
			break;
		ch->sl.statem.disc_status = 2;
		if (occupancy < ch->sl.config.tb_onset_3)
			break;
		ch->sl.statem.cong_status = 3;
		if (occupancy < ch->sl.config.tb_discd_3)
			break;
		ch->sl.statem.disc_status = 3;
		break;
	case 2:
		if (occupancy < ch->sl.config.tb_abate_1) {
			ch->sl.statem.cong_status = 0;
			ch->sl.statem.disc_status = 0;
			break;
		}
		if (occupancy < ch->sl.config.tb_abate_2 || !multi) {
			ch->sl.statem.cong_status = 1;
			ch->sl.statem.disc_status = 1;
			break;
		}
		if (occupancy < ch->sl.config.tb_onset_3)
			break;
		ch->sl.statem.cong_status = 3;
		if (occupancy < ch->sl.config.tb_discd_3)
			break;
		ch->sl.statem.disc_status = 3;
		break;
	case 3:
		if (occupancy < ch->sl.config.tb_abate_1) {
			ch->sl.statem.cong_status = 0;
			ch->sl.statem.disc_status = 0;
			break;
		}
		if (occupancy < ch->sl.config.tb_abate_2 || !multi) {
			ch->sl.statem.cong_status = 1;
			ch->sl.statem.disc_status = 1;
			break;
		}
		if (occupancy < ch->sl.config.tb_abate_3) {
			ch->sl.statem.cong_status = 2;
			ch->sl.statem.disc_status = 2;
			break;
		}
		break;
	}
	if (ch->sl.statem.cong_status != old_cong_status || ch->sl.statem.disc_status != old_disc_status) {
		if (ch->sl.statem.cong_status < old_cong_status)
			return sl_link_congestion_ceased_ind(ch, q, mp);
		else {
			if (ch->sl.statem.cong_status > old_cong_status) {
				if ((ch->sl.notify.events & SL_EVT_CONGEST_ONSET_IND)
				    && !ch->sl.stats.sl_cong_onset_ind[ch->sl.statem.cong_status]++) {
					return sl_link_congested_ind(ch, q, mp);
				}
			} else {
				if ((ch->sl.notify.events & SL_EVT_CONGEST_DISCD_IND)
				    && !ch->sl.stats.sl_cong_discd_ind[ch->sl.statem.disc_status]++) {
					return sl_link_congested_ind(ch, q, mp);
				}
			}
			return sl_link_congested_ind(ch, q, mp);
		}
	}
	freemsg(mp);
	return (0);
}

static inline fastcall __hot_write void
sl_txc_message_for_transmission(struct ch *ch, queue_t *q, mblk_t *mp)
{
	bufq_queue(&ch->sl.tb, mp);
	ch->sl.statem.Cm++;
	sl_check_congestion(ch, q, mp);
}

static inline fastcall __hot_write int
sl_lsc_pdu(struct ch *ch, queue_t *q, mblk_t *mp)
{
	mblk_t *dp = mp;
	int hlen = (ch->option.popt & SS7_POPT_XSN) ? 6 : 3;

	ensure(dp, return (-EFAULT));
	if (ch->sl.tb.q_count > 1024)
		return (-ENOBUFS);
	/* preferred method of sending data is just M_DATA blocks */
	if (likely(DB_TYPE(dp) == M_DATA))
		mp = NULL;
	else
		dp = mp->b_cont;
	ensure(dp, return (-EFAULT));
	/* FIXME: duplicate message (dp) to xray chain here.... */
	if (mp) {
		/* transform M_PROTO block into header */
		mp->b_band = 0;
		mp->b_flag = 0;
		DB_TYPE(mp) = M_DATA;
		mp->b_rptr = DB_BASE(mp);
		mp->b_wptr = mp->b_rptr + hlen;
	} else if (MBLKHEAD(dp) >= hlen) {
		/* enough headroom for header in same block */
		/* this needs to be marked as for transmission somehow */
		mp = dp;
		mp->b_rptr -= hlen;
	} else if ((mp = mi_allocb(q, hlen, BPRI_MED))) {
		/* allocated a new block just for header */
		/* this needs to be marked as for transmission somehow */
		mp->b_cont = dp;
		mp->b_wptr = mp->b_rptr + hlen;
	} else {
		return (-ENOBUFS);
	}
	sl_txc_message_for_transmission(ch, q, mp);
	return (0);
}

noinline fastcall void
sl_aerm_su_in_error(struct ch *ch)
{
	if (ch->sdt.statem.aerm_state == SDT_STATE_MONITORING) {
		ch->sdt.statem.Ca++;
		if (ch->sdt.statem.Ca == ch->sdt.statem.Ti) {
			ch->sdt.statem.aborted_proving = 1;
			sl_iac_abort_proving(ch);
			ch->sdt.statem.Ca--;
			ch->sdt.statem.aerm_state = SDT_STATE_IDLE;
		}
	}
}

static inline fastcall __hot_in void
sl_aerm_correct_su(struct ch *ch)
{
	if (ch->sdt.statem.aerm_state == SDT_STATE_IDLE) {
		if (ch->sdt.statem.aborted_proving) {
			sl_iac_correct_su(ch);
			ch->sdt.statem.aborted_proving = 0;
		}
	}
}

noinline fastcall void
sl_suerm_su_in_error(struct ch *ch)
{
	if (ch->sdt.statem.suerm_state == SDT_STATE_IN_SERVICE) {
		ch->sdt.statem.Cs++;
		if (ch->sdt.statem.Cs >= ch->sdt.config.T) {
			sl_lsc_link_failure(ch, SL_FAIL_SUERM_EIM);
			ch->sdt.statem.Ca--;
			ch->sdt.statem.suerm_state = SDT_STATE_IDLE;
			return;
		}
		ch->sdt.statem.Ns++;
		if (ch->sdt.statem.Ns >= ch->sdt.config.D) {
			ch->sdt.statem.Ns = 0;
			if (ch->sdt.statem.Cs)
				ch->sdt.statem.Cs--;
		}
	}
}

noinline fastcall void
sl_eim_su_in_error(struct ch *ch)
{
	if (ch->sdt.statem.eim_state == SDT_STATE_MONITORING)
		ch->sdt.statem.interval_error = 1;
}

static inline fastcall __hot_in void
sl_suerm_correct_su(struct ch *ch)
{
	if (ch->sdt.statem.suerm_state == SDT_STATE_IN_SERVICE) {
		ch->sdt.statem.Ns++;
		if (ch->sdt.statem.Ns >= ch->sdt.config.D) {
			ch->sdt.statem.Ns = 0;
			if (ch->sdt.statem.Cs)
				ch->sdt.statem.Cs--;
		}
	}
}

static inline fastcall __hot_in void
sl_eim_correct_su(struct ch *ch)
{
	if (ch->sdt.statem.eim_state == SDT_STATE_MONITORING)
		ch->sdt.statem.su_received = 1;
}

static inline fastcall __hot_in void
sl_daedr_correct_su(struct ch *ch)
{
	sl_eim_correct_su(ch);
	sl_suerm_correct_su(ch);
	sl_aerm_correct_su(ch);
}

/*
 *  Hooks to Soft-HDLC
 *  -----------------------------------
 */
noinline fastcall void
sl_daedr_su_in_error(struct ch *ch)
{
	if (ch->sl.statem.lsc_state != SL_STATE_POWER_OFF) {
		sl_eim_su_in_error(ch);
		sl_suerm_su_in_error(ch);
		sl_aerm_su_in_error(ch);
	} else {
		/* cancel compression */
		if (ch->rx.cmp) {
			printd(("SU in error\n"));
			ss7_fast_freemsg(&xp_bufpool, xchg(&ch->rx.cmp, NULL));
		}
	}
	return;
}

/** sl_daedr_received_bits: - process received frames
  * @ch: time slot structure
  * @mp: the received frame
  *
  * This is the point at which we decide whether to deliver the frame at this point or pass it on.
  * When the signalling link is powered up, pass the frame to the signalling link, otherwise,
  * deliver it up as a signalling terminal frame.  This is the point at which we need to decide what
  * to do about monitoring the receive at the SDT level.  We are still in a tasklet context here and
  * we were called from xp_rx_block() which we want to loop through each signalling link's 8 bytes
  * of data.
  * 
  * This is the place to do all monitoring taps.  We have a frame.  For DLT_MTP2_WITH_PHDR stick
  * link info in header and deliver entire frame.  For DLT_MTP, deliver entire frame. For DLT_MTP3,
  * strip the link level header and deliver remaining frame.
  */
static inline fastcall __hot_in void
sl_daedr_received_bits(struct ch *ch, mblk_t *mp)
{
	if (ch->sl.statem.lsc_state != SL_STATE_POWER_OFF) {
		sl_rc_signal_unit(ch, mp);
		sl_daedr_correct_su(ch);
	} else {
		int i, len, mlen = (ch->option.popt & SS7_POPT_XSN) ? 8 : 5;

		if (mp) {
			len = msgdsize(mp);
			if (ch->rx.cmp) {
#if 0
				if (ch->rx.repeat > 50)
					goto no_match;
#endif
				if (len > mlen || len != msgdsize(ch->rx.cmp))
					goto no_match;
				for (i = 0; i < len; i++)
					if (mp->b_rptr[i] != ch->rx.cmp->b_rptr[i])
						goto no_match;
				ch->rx.repeat++;
				ch->sdt.stats.rx_sus_compressed++;
				freemsg(mp);
				return;
			      no_match:
				if (ch->rx.repeat) {
#if 0
					mblk_t *cd;

					if ((cd = dupb(ch->rx.cmp)))
						if (sdt_rc_signal_unit_ind(ch, cd, ch->rx.repeat)) {
							ch->sdt.stats.rx_buffer_overflows++;
							freeb(cd);
						}
#endif
					ch->rx.repeat = 0;
				}
			}
			if (len <= mlen) {
				if (ch->rx.cmp || (ch->rx.cmp = ss7_fast_allocb(&xp_bufpool, mlen, BPRI_HI))) {
					bcopy(mp->b_rptr, ch->rx.cmp->b_rptr, len);
					ch->rx.cmp->b_wptr = ch->rx.cmp->b_rptr + len;
					ch->rx.repeat = 0;
				}
			}
			if (sdt_rc_signal_unit_ind(ch, mp, 1)) {
				ch->sdt.stats.rx_buffer_overflows++;
				freemsg(mp);
			}
		} else
			swerr();
	}
}

static inline fastcall __hot_out mblk_t *
sl_daedt_transmission_request(struct ch *ch)
{
	mblk_t *mp;

	if (ch->sl.statem.lsc_state != SL_STATE_POWER_OFF) {
		if (!(mp = sl_txc_transmission_request(ch)))
			ch->sdt.stats.tx_buffer_overflows++;
		return (mp);
	} else if (ch->sdt.statem.daedt_state != SDT_STATE_IDLE) {
		if ((mp = bufq_dequeue(&ch->sdt.tb))) {
			int len = msgdsize(mp);
			int hlen = (ch->option.popt & SS7_POPT_XSN) ? 6 : 3;
			int mlen = hlen + 2;

			if (!ch->sdt.tb.q_count < 512 && sl_count_rq(ch))
				sl_enable_rq(ch);	/* back-enable */
			if (mlen < len)
				goto dont_repeat;
			if (mlen == len + 1 || mlen == len + 2) {
				int li, sio;

				if (ch->option.popt & SS7_POPT_XSN) {
					li = ((mp->b_rptr[5] << 8) | mp->b_rptr[4]) & 0x1ff;
					sio = mp->b_rptr[6];
				} else {
					li = mp->b_rptr[2] & 0x3f;
					sio = mp->b_rptr[3];
				}
				if (li != mlen - len)
					goto dont_repeat;
				switch (sio) {
				case LSSU_SIO:
				case LSSU_SIN:
				case LSSU_SIE:
				case LSSU_SIOS:
				case LSSU_SIPO:
					break;
				case LSSU_SIB:
				default:
					goto dont_repeat;
				}
			}
			if (ch->tx.cmp || (ch->tx.cmp = ss7_fast_allocb(&xp_bufpool, mlen, BPRI_HI))) {
				mblk_t *cd = ch->tx.cmp;

				if (len > mlen)
					len = hlen;
				cd->b_rptr = DB_BASE(cd);
				bcopy(mp->b_rptr, cd->b_rptr, len);
				cd->b_wptr = cd->b_rptr + len;
				/* always correct length indicator */
				if (ch->option.popt & SS7_POPT_XSN) {
					cd->b_rptr[4] &= 0x00;
					cd->b_rptr[5] &= 0xfe;
					cd->b_rptr[4] += (len - hlen);
				} else {
					cd->b_rptr[2] &= 0xc0;
					cd->b_rptr[2] += (len - hlen);
				}
				ch->tx.repeat = 0;
				return (mp);
			}
		      dont_repeat:
			if (ch->tx.cmp)
				ss7_fast_freemsg(&xp_bufpool, xchg(&ch->tx.cmp, NULL));
		} else {
			if ((mp = ch->tx.cmp)) {
				mp->b_rptr = DB_BASE(mp);
				ch->sdt.stats.tx_sus_repeated++;
				ch->tx.repeat++;
			} else
				ch->sdt.stats.tx_buffer_overflows++;
		}
		return (mp);
	} else if (ch->sdl.statem.tx_state != SDL_STATE_IDLE) {
		if ((mp = bufq_dequeue(&ch->sdl.tb))) {
			if (ch->sdl.tb.q_count < 32 && sl_count_rq(ch))
				sl_enable_rq(ch);
		} else
			ch->sdl.stats.tx_buffer_overflows++;
		return (mp);
	} else {
		return (NULL);
	}
}

/*
 *  ========================================================================
 *
 *  Events from below
 *
 *  ========================================================================
 */
/*
 *  -------------------------------------------------------------------------
 *
 *  SL events from below (timeouts)
 *
 *  -------------------------------------------------------------------------
 *  Timeouts need to be reworked so that they are independent of a queue.
 */

/** xp_t1_timeout: - T1 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t1_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T1 TIMEOUT <-");
	ch->sl.statem.failure_reason = SL_FAIL_T1_TIMEOUT;
	sl_out_of_service_ind(ch);
	sl_rc_stop(ch);
	sl_suerm_stop(ch);
	sl_txc_send_sios(ch);
	ch->sl.statem.emergency = 0;
	if (ch->sl.statem.lsc_state == SL_STATE_ALIGNED_NOT_READY) {
		sl_poc_stop(ch);	/* ok if ANSI */
		ch->sl.statem.local_processor_outage = 0;
	}
	ch->sl.statem.lsc_state = SL_STATE_OUT_OF_SERVICE;
}

/** xp_t2_timeout: - T2 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t2_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T2 TIMEOUT <-");
	if (ch->sl.statem.iac_state == SL_STATE_NOT_ALIGNED) {
		sl_lsc_alignment_not_possible(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.iac_state = SL_STATE_IDLE;
	}
}

/** xp_t3_timeout: - T3 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t3_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T3 TIMEOUT <-");
	if (ch->sl.statem.iac_state == SL_STATE_ALIGNED) {
		sl_lsc_alignment_not_possible(ch);
		ch->sl.statem.emergency = 0;
		ch->sl.statem.iac_state = SL_STATE_IDLE;
	}
}

/** xp_t4_timeout: - T4 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t4_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T4 TIMEOUT <-");
	if (ch->sl.statem.iac_state == SL_STATE_PROVING) {
		if (ch->sl.statem.further_proving) {
			sl_aerm_start(ch);
			xp_timer_start(ch, t4);
			ch->sl.statem.further_proving = 0;
		} else {
			sl_lsc_alignment_complete(ch);
			sl_aerm_stop(ch);
			ch->sl.statem.emergency = 0;
			ch->sl.statem.iac_state = SL_STATE_IDLE;
		}
	}
}

/** xp_t5_timeout: - T5 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t5_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T5 TIMEOUT <-");
	if (ch->sl.statem.cc_state == SL_STATE_BUSY) {
		sl_txc_send_sib(ch);
		xp_timer_start(ch, t5);
	}
}

/** xp_t6_timeout: - T6 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t6_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T6 TIMEOUT <-");
	sl_lsc_link_failure(ch, SL_FAIL_CONG_TIMEOUT);
	ch->sl.statem.sib_received = 0;
	xp_timer_stop(ch, t7);
}

/** xp_t7_timeout: - T7 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall __unlikely void
xp_t7_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T7 TIMEOUT <-");
	sl_lsc_link_failure(ch, SL_FAIL_ACK_TIMEOUT);
	xp_timer_stop(ch, t6);
	if (((ch->option.pvar & SS7_PVAR_MASK) != SS7_PVAR_ANSI)
	    || ((ch->option.pvar & SS7_PVAR_YR) <= SS7_PVAR_88))
		ch->sl.statem.sib_received = 0;
}

/** xp_t8_timeout: - T8 expiry
  * @ch: channel structure (locked)
  */
noinline fastcall void
xp_t8_timeout(struct ch *ch)
{
	LOGTO(ch2sid(ch), "-> T8 TIMEOUT <-");
	if (ch->sdt.statem.eim_state == SDT_STATE_MONITORING) {
		xp_timer_start(ch, t8);
		if (ch->sdt.statem.su_received) {
			ch->sdt.statem.su_received = 0;
			if (!ch->sdt.statem.interval_error) {
				if ((ch->sdt.statem.Ce -= ch->sdt.config.De) < 0)
					ch->sdt.statem.Ce = 0;
				return;
			}
		}
		ch->sdt.statem.Ce += ch->sdt.config.Ue;
		if (ch->sdt.statem.Ce > ch->sdt.config.Te) {
			sl_lsc_link_failure(ch, SL_FAIL_SUERM_EIM);
			ch->sdt.statem.eim_state = SDT_STATE_IDLE;
		}
		ch->sdt.statem.interval_error = 0;
	}
}

static inline fastcall __unlikely void
xp_expiry(struct ch *ch, const uint t)
{
	spin_lock_bh(&ch->lock);
	{
		switch (t) {
		case t1:
			if (likely(ch->sl.timers.t1 != 0))
				xp_t1_timeout(ch);
			break;
		case t2:
			if (likely(ch->sl.timers.t2 != 0))
				xp_t2_timeout(ch);
			break;
		case t3:
			if (likely(ch->sl.timers.t3 != 0))
				xp_t3_timeout(ch);
			break;
		case t4:
			if (likely(ch->sl.timers.t4 != 0))
				xp_t4_timeout(ch);
			break;
		case t5:
			if (likely(ch->sl.timers.t5 != 0))
				xp_t5_timeout(ch);
			break;
		case t6:
			if (likely(ch->sl.timers.t6 != 0))
				xp_t6_timeout(ch);
			break;
		case t7:
			if (likely(ch->sl.timers.t7 != 0))
				xp_t7_timeout(ch);
			break;
		case t8:
			if (likely(ch->sdt.timers.t8 != 0))
				xp_t8_timeout(ch);
			break;
#if 0
		case t9:
			if (likely(ch->sdl.timers.t9 != 0))
				xp_t9_timeout(ch);
			break;
#endif
		default:
			swerr();
			break;
		}
	}
	spin_unlock_bh(&ch->lock);
}

streamscall void
xp_t1_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t1);
}

streamscall void
xp_t2_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t2);
}

streamscall void
xp_t3_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t3);
}

streamscall void
xp_t4_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t4);
}

streamscall void
xp_t5_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t5);
}

streamscall void
xp_t6_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t6);
}

streamscall void
xp_t7_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t7);
}

streamscall void
xp_t8_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t8);
}

#if 0
streamscall void
xp_t9_expiry(caddr_t data)
{
	xp_expiry((struct ch *) data, t9);
}
#endif

/*
 *  ========================================================================
 *
 *  Soft-HDLC
 *
 *  ========================================================================
 */
#define SDT_TX_STATES	5
#define SDT_RX_STATES	16

#define SDT_TX_BUFSIZE	PAGE_SIZE
#define SDT_RX_BUFSIZE	PAGE_SIZE

#define SDT_CRC_TABLE_LENGTH	512
#define SDT_TX_TABLE_LENGTH	(2* SDT_TX_STATES * 256)
#define SDT_RX_TABLE_LENGTH	(2* SDT_RX_STATES * 256)

typedef struct tx_entry {
	uint bit_string:10 __attribute__ ((packed));	/* the output string */
	uint bit_length:2 __attribute__ ((packed));	/* length in excess of 8 bits of output string */
	uint state:3 __attribute__ ((packed));	/* new state */
} tx_entry_t;

typedef struct rx_entry {
	uint bit_string:12 __attribute__ ((packed));
	uint bit_length:4 __attribute__ ((packed));
	uint state:4 __attribute__ ((packed));
	uint sync:1 __attribute__ ((packed));
	uint hunt:1 __attribute__ ((packed));
	uint flag:1 __attribute__ ((packed));
	uint idle:1 __attribute__ ((packed));
} rx_entry_t;

typedef uint16_t bc_entry_t;

STATIC bc_entry_t *bc_table = NULL;
STATIC tx_entry_t *tx_table = NULL;
STATIC rx_entry_t *rx_table = NULL;
STATIC rx_entry_t *rx_table7 = NULL;

STATIC size_t bc_order = 0;
STATIC size_t tx_order = 0;
STATIC size_t rx_order = 0;

STATIC INLINE tx_entry_t *
tx_index(uint j, uint k)
{
	return &tx_table[(j << 8) | k];
}

STATIC INLINE rx_entry_t *
rx_index7(uint j, uint k)
{
	return &rx_table7[(j << 8) | k];
}

STATIC INLINE rx_entry_t *
rx_index8(uint j, uint k)
{
	return &rx_table[(j << 8) | k];
}

/*
 *  TX BITSTUFF
 *  -----------------------------------
 *  Bitstuff an octet and shift residue for output.
 */
static inline fastcall __hot_out void
xp_tx_bitstuff(xp_path_t * tx, uchar byte)
{
	tx_entry_t *t = tx_index(tx->state, byte);

	tx->state = t->state;
	tx->residue <<= 8 + t->bit_length;
	tx->residue |= t->bit_string;
	tx->rbits += 8 + t->bit_length;
}

#define TX_MODE_IDLE	0	/* generating mark idle */
#define TX_MODE_FLAG	1	/* generating flag idle */
#define TX_MODE_BOF	2	/* generating bof flag */
#define TX_MODE_MOF	3	/* generating frames */
#define TX_MODE_BCC	4	/* generating bcc bytes */

/** xp_tx_block: - TX BLOCK
  * @ch: channel structure (locked)
  * @bp: beg tx block pointer (with offset)
  * @be: end tx block pointer (without offset)
  * @stats: channel statistics
  * @type: channel type
  *
  * Generate a block for transmission for a channel or span.  We generate an entire transmit block.
  * If there are not sufficient messages to build the transmit block we will repeat FISU/LSSU or
  * idle flags.
  */
noinline fastcall __hot_out void
xp_tx_block(struct ch *ch, uchar *bp, uchar *be, sdt_stats_t * stats, const sl_ulong type)
{
	register xp_path_t *tx = &ch->tx;
	int chan = 1;

	if (ch->sdt.statem.daedt_state != SDT_STATE_IDLE) {
		if (tx->mode == TX_MODE_IDLE || tx->mode == TX_MODE_FLAG) {
			if (!tx->nxt) {
			      next_message:
				if (tx->msg && tx->msg != tx->cmp)
					freemsg(tx->msg);
				if ((tx->msg = tx->nxt = sl_daedt_transmission_request(ch)))
					tx->mode = TX_MODE_BOF;
			}
		}
		/* check if transmission block complete */
		for (; bp < be; bp += 128) {
		      check_rbits:
			/* drain residue bits, if necessary */
			if (tx->rbits >= 8) {
			      drain_rbits:
				/* drain residue bits */
				tx->rbits -= 8;
				ch->sdl.stats.tx_octets++;
				switch (type) {
				default:
				case SDL_TYPE_DS0:
					/* is this & 0xFE or & 0x7F ? */
					*bp = tx->residue >> tx->rbits;
					break;
				case SDL_TYPE_DS0A:
					*bp = tx->residue >> tx->rbits;
					tx->rbits += 1;
					break;
				case SDL_TYPE_T1:
					*(bp + (xp_t1_chan_map[chan] << 2)) = tx->residue >> tx->rbits;
					if (++chan > 24)
						chan = 1;
					break;
				case SDL_TYPE_J1:
					*(bp + (xp_t1_chan_map[chan] << 2)) = tx->residue >> tx->rbits;
					tx->rbits += 1;
					if (++chan > 24)
						chan = 1;
					break;
				case SDL_TYPE_E1:
					*(bp + (xp_e1_chan_map[chan] << 2)) = tx->residue >> tx->rbits;
					if (++chan > 31)
						chan = 1;
					break;
				}
				if (chan > 1)
					goto check_rbits;
				continue;
			}
			switch (tx->mode) {
			case TX_MODE_IDLE:
				/* mark idle */
				tx->residue <<= 8;
				tx->residue |= 0xff;
				tx->rbits += 8;
				goto drain_rbits;
			case TX_MODE_FLAG:
				/* idle flags */
				tx->residue <<= 8;
				tx->residue |= 0x7e;
				tx->rbits += 8;
				goto drain_rbits;
			case TX_MODE_BOF:
				/* add opening flag (also closing flag) */
				switch (ch->sdt.config.f) {
				default:
				case SDT_FLAGS_ONE:
					tx->residue <<= 8;
					tx->residue |= 0x7e;
					tx->rbits += 8;
					break;
				case SDT_FLAGS_SHARED:
					tx->residue <<= 15;
					tx->residue |= 0x3f7e;
					tx->rbits += 15;
					break;
				case SDT_FLAGS_TWO:
					tx->residue <<= 16;
					tx->residue |= 0x7e7e;
					tx->rbits += 16;
					break;
				case SDT_FLAGS_THREE:
					tx->residue <<= 24;
					tx->residue |= 0x7e7e7e;
					tx->rbits += 24;
					break;
				}
				tx->state = 0;
				tx->bcc = 0xffff;
				tx->mode = TX_MODE_MOF;
				goto drain_rbits;
			case TX_MODE_MOF:	/* transmit frame bytes */
				if (tx->nxt->b_rptr < tx->nxt->b_wptr || (tx->nxt = tx->nxt->b_cont)) {
					/* continuing in message */
					uint byte = *tx->nxt->b_rptr++;

					tx->bcc = (tx->bcc >> 8) ^ bc_table[(tx->bcc ^ byte) & 0x00ff];
					xp_tx_bitstuff(tx, byte);
					stats->tx_bytes++;
					goto drain_rbits;
				}
				/* finished message: add 1st bcc byte */
				tx->bcc = ~(tx->bcc);
				xp_tx_bitstuff(tx, tx->bcc);
				tx->mode = TX_MODE_BCC;
				goto drain_rbits;
			case TX_MODE_BCC:
				/* add 2nd bcc byte */
				xp_tx_bitstuff(tx, tx->bcc >> 8);
				stats->tx_sus++;
				tx->mode = TX_MODE_FLAG;
				goto next_message;
			}
			swerr();
		}
	} else if (ch->sdl.statem.tx_state != SDL_STATE_IDLE) {
		for (; bp < be; bp += 128) {
			do {
				if (tx->nxt) {
					do {
						if (tx->nxt->b_rptr < tx->nxt->b_wptr)
							goto tx_process_block;
					} while ((tx->nxt = tx->nxt->b_cont));
				}
				/* next block */
				if (tx->msg)
					ss7_fast_freemsg(&xp_bufpool, tx->msg);
				if (!(tx->msg = tx->nxt = sl_daedt_transmission_request(ch))) {
					ch->sdl.stats.tx_buffer_overflows++;
					return;
				}
			      tx_process_block:
				switch (type) {
				default:
				case SDL_TYPE_DS0:
				case SDL_TYPE_DS0A:
					*bp = *tx->nxt->b_rptr++;
					chan = 1;
					break;
				case SDL_TYPE_T1:
				case SDL_TYPE_J1:
					*(bp + (xp_t1_chan_map[chan] << 2)) = *tx->nxt->b_rptr++;
					if (++chan > 24)
						chan = 1;
					break;
				case SDL_TYPE_E1:
					*(bp + (xp_e1_chan_map[chan] << 2)) = *tx->nxt->b_rptr++;
					if (++chan > 31)
						chan = 1;
					break;
				}
				ch->sdl.stats.tx_octets++;
				while (tx->nxt && tx->nxt->b_rptr >= tx->nxt->b_wptr)
					tx->nxt = tx->nxt->b_cont;
			}
			while (chan > 1);
		}
	}
}

/** xp_tx_idle: - TX IDLE
  * @bp: beg tx block pointer (with offset)
  * @be: end tx block pointer (without offset)
  * @type: channel type
  *
  * Writes idle (silent) code for a given channel.  We should be able to get rid of this routine
  * when we are properly using the per-channel on-chip idle code generation features of the Dallas
  * framer chips.
  *
  * TODO: Use the framer chip to automagically generate idle code instead of using this function.
  */
noinline fastcall __hot_out void
xp_tx_idle(uchar *bp, uchar *be, const sl_ulong type)
{
	int chan;

	for (; bp < be; bp += 128) {
		switch (type) {
		default:
		case SDL_TYPE_DS0:
			*bp = 0xff;
			continue;
		case SDL_TYPE_DS0A:
			*bp = 0x7f;
			continue;
		case SDL_TYPE_T1:
		case SDL_TYPE_J1:
			for (chan = 1; chan <= 24; chan++)
				*(bp + (xp_t1_chan_map[chan] << 2)) = 0x7f;
			continue;
		case SDL_TYPE_E1:
			for (chan = 1; chan <= 31; chan++)
				*(bp + (xp_e1_chan_map[chan] << 2)) = 0xff;
			continue;
		}
	}
}

/** xp_rx_linkb: - link a buffer
  * @rx: rx path structure pointer
  *
  * Link a buffer to existing message or create new message with buffer.
  */
static inline fastcall __hot_in void
xp_rx_linkb(xp_path_t * rx)
{
	if (rx->msg)
		linkb(rx->msg, rx->nxt);
	else
		rx->msg = rx->nxt;
	rx->nxt = NULL;
	return;
}

#define RX_MODE_IDLE	0
#define RX_MODE_HUNT	1	/* hunting for flags */
#define RX_MODE_SYNC	2	/* between frames */
#define RX_MODE_MOF	3	/* middle of frame */

/** xp_rx_block: - RX BLOCK
  * @ch: channel structure (locked)
  * @bp: beg rx block pointer (with offset)
  * @be: end rx block pointer (without offset)
  * @stats: channel statistics
  * @type: channel type
  *
  * Process a receive block for a channel or span.  We process all of the octets in the receive
  * block.  Any complete messages will be delivered to the upper layer if the upper layer is not
  * congested.  If the upper layer is congested we discard the message and indicate receive
  * congestion.  The upper layer should be sensitive to its receive queue backlog and start sending
  * SIB when required.  We do not use backenabling from the upper layer.  We merely start discarding
  * complete messages when the upper layer is congested.
  */
noinline fastcall __hot_in void
xp_rx_block(struct ch *ch, uchar *bp, uchar *be, sdt_stats_t * stats, const sl_ulong type)
{
	int chan = 1;
	register xp_path_t *rx = &ch->rx;

	if (ch->sdt.statem.daedr_state != SDT_STATE_IDLE) {
		int xsn = ch->option.popt & SS7_POPT_XSN;
		int M, mlen, hlen, N = ch->sdt.config.N;

		if (!xsn) {
			hlen = 3;
			mlen = 0x3f;
		} else {
			hlen = 6;
			mlen = 0x1ff;
		}
		M = ch->sdt.config.m + 1 + hlen;
		for (; bp < be; bp += 128) {
			rx_entry_t *r;

		      next_channel:
			ch->sdl.stats.rx_octets++;
			switch (type) {
			default:
			case SDL_TYPE_DS0:
				_printd(("Rx: %p: 0x%02x\n", ch, *bp));
				r = rx_index8(rx->state, *bp);
				break;
			case SDL_TYPE_DS0A:
				r = rx_index7(rx->state, *bp);
				break;
			case SDL_TYPE_T1:
				r = rx_index8(rx->state, *(bp + (xp_t1_chan_map[chan] << 2)));
				if (++chan > 24)
					chan = 1;
				break;
			case SDL_TYPE_J1:
				r = rx_index7(rx->state, *(bp + (xp_t1_chan_map[chan] << 2)));
				if (++chan > 24)
					chan = 1;
				break;
			case SDL_TYPE_E1:
				r = rx_index8(rx->state, *(bp + (xp_e1_chan_map[chan] << 2)));
				if (++chan > 31)
					chan = 1;
				break;
			}
			rx->state = r->state;
			switch (rx->mode) {
			case RX_MODE_MOF:
				if (!r->sync && r->bit_length) {
					rx->residue |= r->bit_string << rx->rbits;
					rx->rbits += r->bit_length;
				}
				if (!r->flag) {
					if (r->hunt || r->idle)
						goto aborted;
					while (rx->rbits > 16) {
						if (rx->nxt && rx->nxt->b_wptr >= DB_LIM(rx->nxt))
							xp_rx_linkb(rx);
						if (!rx->nxt
						    && !(rx->nxt =
							 ss7_fast_allocb(&xp_bufpool, FASTBUF, BPRI_HI)))
							goto buffer_overflow;
						rx->bcc = (rx->bcc >> 8)
						    ^ bc_table[(rx->bcc ^ rx->residue) & 0x00ff];
						*rx->nxt->b_wptr++ = rx->residue;
						stats->rx_bytes++;
						rx->residue >>= 8;
						rx->rbits -= 8;
						rx->bytes++;
						if (rx->bytes > M)
							goto frame_too_long;
					}
				} else {
					uint len, li;

					if (rx->rbits != 16)
						goto residue_error;
					if (((~rx->bcc) & 0xffff) != (rx->residue & 0xffff))
						goto crc_error;
					if (rx->bytes < hlen)
						goto frame_too_short;
					xp_rx_linkb(rx);
					if (!xsn)
						li = rx->msg->b_rptr[2] & mlen;
					else
						li = ((rx->msg->b_rptr[5] << 8) | rx->msg->b_rptr[4]) & mlen;
					len = rx->bytes - hlen;
					if (len != li && (li != mlen || len <= li))
						goto length_error;
					stats->rx_sus++;
					sl_daedr_received_bits(ch, xchg(&rx->msg, NULL));
				      new_frame:
					rx->mode = RX_MODE_SYNC;
					rx->residue = 0;
					rx->rbits = 0;
					rx->bytes = 0;
					rx->bcc = 0xffff;
					if (r->sync) {
					      begin_frame:
						if (r->bit_length) {
							rx->mode = RX_MODE_MOF;
							rx->residue = r->bit_string;
							rx->rbits = r->bit_length;
							rx->bytes = 0;
							rx->bcc = 0xffff;
						}
					}
				}
				break;
			      frame_too_long:
				stats->rx_frame_too_long++;
				stats->rx_frame_errors++;
				goto abort_frame;
			      buffer_overflow:
				stats->rx_buffer_overflows++;
				goto abort_frame;
			      aborted:
				stats->rx_aborts++;
				stats->rx_frame_errors++;
				goto abort_frame;
			      length_error:
				stats->rx_length_error++;
				goto abort_frame;
			      frame_too_short:
				stats->rx_frame_too_short++;
				stats->rx_frame_errors++;
				goto abort_frame;
			      crc_error:
				stats->rx_crc_errors++;
				goto abort_frame;
			      residue_error:
				stats->rx_residue_errors++;
				stats->rx_frame_errors++;
				goto abort_frame;
			      abort_frame:
				if (rx->nxt)
					ss7_fast_freemsg(&xp_bufpool, xchg(&rx->nxt, NULL));
				if (rx->msg)
					ss7_fast_freemsg(&xp_bufpool, xchg(&rx->msg, NULL));
				stats->rx_sus_in_error++;
				sl_daedr_su_in_error(ch);
				if (r->flag)
					goto new_frame;
				goto start_hunt;
			case RX_MODE_SYNC:
				if (!r->hunt && !r->idle)
					goto begin_frame;
			      start_hunt:
				if (r->idle)
					rx->mode = RX_MODE_IDLE;
				else
					rx->mode = RX_MODE_HUNT;
				stats->rx_sync_transitions++;
				rx->octets = 0;
				break;
			case RX_MODE_IDLE:
				if (!r->idle)
					rx->mode = RX_MODE_HUNT;
				/* fall through */
			case RX_MODE_HUNT:
				if (!r->flag) {
					if (r->idle && rx->mode != RX_MODE_IDLE)
						rx->mode = RX_MODE_IDLE;
					if ((++rx->octets) >= N) {
						stats->rx_sus_in_error++;
						sl_daedr_su_in_error(ch);
						rx->octets -= N;
					}
					stats->rx_bits_octet_counted += 8;
					break;
				}
				stats->rx_sync_transitions++;
				goto new_frame;
			default:
				swerr();
				goto abort_frame;
			}
			if (chan > 1)
				goto next_channel;
		}
	} else if (ch->sdl.statem.rx_state != SDL_STATE_IDLE) {
		for (; bp < be; bp += 128) {
			do {
				if (rx->nxt) {
					if (rx->nxt->b_wptr < rx->nxt->b_rptr + ch->sdl.config.ifblksize)
						goto rx_process_block;
					if (sdl_received_bits_ind(ch, rx->nxt)) {
						ch->sdl.stats.rx_buffer_overflows++;
						goto rx_process_block;
					}
				}
				/* new block */
				if (!(rx->nxt = ss7_fast_allocb(&xp_bufpool, FASTBUF, BPRI_HI))) {
					ch->sdl.stats.rx_buffer_overflows++;
					return;
				}
			      rx_process_block:
				switch (type) {
				default:
				case SDL_TYPE_DS0:
				case SDL_TYPE_DS0A:
					*rx->nxt->b_wptr++ = *bp;
					chan = 1;
					break;
				case SDL_TYPE_T1:
				case SDL_TYPE_J1:
					*rx->nxt->b_wptr++ = *(bp + (xp_t1_chan_map[chan] << 2));
					if (++chan > 24)
						chan = 1;
					break;
				case SDL_TYPE_E1:
					*rx->nxt->b_wptr++ = *(bp + (xp_e1_chan_map[chan] << 2));
					if (++chan > 31)
						chan = 1;
					break;
				}
				ch->sdl.stats.rx_octets++;
			} while (chan > 1);
		}
	}
}

/*
 *  -------------------------------------------------------------------------
 *
 *  Table allocation and generation
 *
 *  -------------------------------------------------------------------------
 *  All Soft HDLC lookup stables are generated at module load time.  This permits the tables to be
 *  page-aligned in kernel memory for maximum cache performance.
 */

/** bc_table_value: - BC (Block Check) CRC Table Entries:
  * @bit_string: the bit string to block check
  * @bit_length: the length (in bits) of the bit string
  *
  * RC tables perform CRC calculation on received bits after zero deletion and delimitation.
  */
STATIC __devinit bc_entry_t
bc_table_value(int bit_string, int bit_length)
{
	int pos;

	for (pos = 0; pos < bit_length; pos++) {
		if (bit_string & 0x1)
			bit_string = (bit_string >> 1) ^ 0x8408;
		else
			bit_string >>= 1;
	}
	return (bit_string);
}

/** tx_table_valueN: - TX (Transmission) Table Entries:
  * @state: transmitter state
  * @byte: byte to transmit
  * @len: number of bits in byte (7 or 8).
  *
  * TX table performs zero insertion and bit reversal on frame and CRC bit streams.
  */
static __devinit struct tx_entry
tx_table_valueN(int state, uint8_t byte, int len)
{
	struct tx_entry result = { 0, };
	int bit_mask = 0x80;

	result.state = state;
	result.bit_length = 0;
	while (len--) {
		if (byte & 0x01) {
			result.bit_string |= bit_mask;
			if (result.state++ == 4) {
				result.state = 0;
				result.bit_length++;
				result.bit_string <<= 1;
			}
		} else
			result.state = 0;
		bit_mask >>= 1;
		byte >>= 1;
	}
	return result;
}

/** tx_table_value: - TX (Transmission) Table Entries
  * @state: transmitter state
  * @byte: byte to transmit
  *
  * TX table performs zero insertion and bit reversal on frame and CRC bit streams.
  */
STATIC __devinit tx_entry_t
tx_table_value(int state, uint8_t byte)
{
	return tx_table_valueN(state, byte, 8);
}

/** rx_table_valueN: - RX (Receive) Table Entries:
  * @state: receiver state
  * @byte: byte received
  * @len: number of bits in byte (7 or 8)
  *
  * RX table performs zero deletion, flag and abort detection, BOF and EOF detection, residue, and
  * bit reversal on received bit streams.
  */
static __devinit struct rx_entry
rx_table_valueN(int state, uint8_t byte, int len)
{
	struct rx_entry result = { 0, };
	int bit_mask = 1;

	result.state = state;
	while (len--) {
		switch (result.state) {
		case 0:	/* 0 *//* zero not belonging to shared flag nor stuffing bit deletion */
			if (byte & 0x80) {
				result.state = 1;
			} else {
				bit_mask <<= 1;
				result.bit_length += 1;
				result.state = 0;
			}
			break;
		case 1:	/* 01 */
			if (byte & 0x80) {
				result.state = 2;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 2;
				result.state = 0;
			}
			break;
		case 2:	/* 011 */
			if (byte & 0x80) {
				result.state = 3;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 3;
				result.state = 0;
			}
			break;
		case 3:	/* 0111 */
			if (byte & 0x80) {
				result.state = 4;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 4;
				result.state = 0;
			}
			break;
		case 4:	/* 01111 */
			if (byte & 0x80) {
				result.state = 5;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 5;
				result.state = 0;
			}
			break;
		case 5:	/* 011111 */
			if (byte & 0x80) {
				result.state = 7;
			} else {
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 6;
				result.state = 6;
			}
			break;
		case 6:	/* [0]11111[0] *//* bit deletion */
			if (byte & 0x80) {
				result.state = 9;
			} else {
				result.state = 0;
			}
			break;
		case 7:	/* 0111111 */
			result.sync = 0;
			result.idle = 0;
			if (byte & 0x80) {
				bit_mask <<= 1;
				result.bit_length += 1;
				result.flag = 0;
				result.hunt = 1;
				result.state = 15;
			} else {
				result.flag = 1;
				result.hunt = 0;
				result.state = 8;
			}
			break;
		case 8:	/* 0111110 */
			bit_mask = 1;
			result.bit_string = 0;
			result.bit_length = 0;
			result.sync = 1;
			result.flag = 1;
			result.hunt = 0;
			result.idle = 0;
			if (byte & 0x80) {
				result.state = 9;
			} else {
				result.state = 0;
			}
			break;
		case 9:	/* [0]1 *//* zero from end of flag or bit deletion */
			result.idle = 0;
			if (byte & 0x80) {
				result.state = 10;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 1;
				result.state = 0;
			}
			break;
		case 10:	/* [0]11 */
			result.idle = 0;
			if (byte & 0x80) {
				result.state = 11;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 2;
				result.state = 0;
			}
			break;
		case 11:	/* [0]111 */
			result.idle = 0;
			if (byte & 0x80) {
				result.state = 12;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 3;
				result.state = 0;
			}
			break;
		case 12:	/* [0]1111 */
			result.idle = 0;
			if (byte & 0x80) {
				result.state = 13;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 4;
				result.state = 0;
			}
			break;
		case 13:	/* [0]11111 */
			result.idle = 0;
			if (byte & 0x80) {
				result.state = 14;
			} else {
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_string |= bit_mask;
				bit_mask <<= 1;
				result.bit_length += 5;
				result.state = 6;
			}
			break;
		case 14:	/* [0]111111 */
			result.sync = 0;
			result.idle = 0;
			if (byte & 0x80) {
				result.flag = 0;
				result.hunt = 1;
				result.state = 15;
			} else {
				result.flag = 1;
				result.hunt = 0;
				result.state = 8;
			}
			break;
		case 15:	/* ...1111111 *//* 7 ones (or 8 ones) */
			result.sync = 0;
			result.flag = 0;
			result.hunt = 1;
			if (byte & 0x80) {
				result.idle = 1;
				result.state = 15;
			} else {
				result.idle = 0;
				result.state = 0;
			}
			break;
		}
		byte <<= 1;
	}
	return result;
}

/** rx_table_value7: - RX (Receive) Table Entries (7-bit table)
  * @state: receiver state
  * @byte: byte received
  *
  * RX table performs zero deletion, flag and abort detection, BOF and EOF detection, residue, and
  * bit reversal on received bit streams.
  */
STATIC __devinit rx_entry_t
rx_table_value7(int state, uint8_t byte)
{
	return rx_table_valueN(state, byte, 7);
}

/** rx_table_value7: - RX (Receive) Table Entries (8-bit table)
  * @state: receiver state
  * @byte: byte received
  *
  * RX table performs zero deletion, flag and abort detection, BOF and EOF detection, residue, and
  * bit reversal on received bit streams.
  */
STATIC __devinit rx_entry_t
rx_table_value8(int state, uint8_t byte)
{
	return rx_table_valueN(state, byte, 8);
}

/** tx_table_generate: - TX (Transmit) Table
  *
  * There is one TX table for 8-bit (octet) output values.  The table has 256 entries and is used to
  * perform, for one sample, zero insertion on frame bits for the transmitted bitstream.
  */
STATIC __devinit void
tx_table_generate(void)
{
	int j, k;

	for (j = 0; j < SDT_TX_STATES; j++)
		for (k = 0; k < 256; k++)
			*tx_index(j, k) = tx_table_value(j, k);
}

/** rx_table_generate7: - RX (Received) Tables (7-bit table)
  *
  * There are two RX tables: one for 8 bit (octet) values, another for 7 bit (DS0A operation).  Each
  * table has 256 entries and is used to perform, for one sample, zero deletion, abort detection,
  * flag detection and residue calculation on the received bitstream.
  */
STATIC __devinit void
rx_table_generate7(void)
{
	int j, k;

	for (j = 0; j < SDT_RX_STATES; j++)
		for (k = 0; k < 256; k++)
			*rx_index7(j, k) = rx_table_value7(j, k);
}

/** rx_table_generate8: - RX (Received) Tables (8-bit table)
  *
  * There are two RX tables: one for 8 bit (octet) values, another for 7 bit (DS0A operation).  Each
  * table has 256 entries and is used to perform, for one sample, zero deletion, abort detection,
  * flag detection and residue calculation on the received bitstream.
  */
STATIC __devinit void
rx_table_generate8(void)
{
	int j, k;

	for (j = 0; j < SDT_RX_STATES; j++)
		for (k = 0; k < 256; k++)
			*rx_index8(j, k) = rx_table_value8(j, k);
}

/** bc_table_generate: - BC (CRC) Tables
  *
  * CRC table organization:  This is a CRC table which contains lookups for all bit lengths less
  * than or equal to 8.  There are 512 entries.  The first 256 entries are for 8-bit bit lengths,
  * the next 128 entries are for 7-bit bit lengths, the next 64 entries for 6-bit bit lengths, etc.
  */
STATIC __devinit void
bc_table_generate(void)
{
	int pos = 0, bit_string, bit_length = 8, bit_mask = 0x100;

	do {
		for (bit_string = 0; bit_string < bit_mask; bit_string++, pos++)
			bc_table[pos] = bc_table_value(bit_string, bit_length);
		bit_length--;
	} while ((bit_mask >>= 1));
}

/** xp_init_tables: - Table allocation and generation.
  *
  * There are 4 mostly read-only tables to allocate and initialize for SOFT-HDLC operation.  This
  * procedure allocated and initializes (calculates) the tables.
  */
noinline __devinit int
xp_init_tables(void)
{
	size_t length;

	length = SDT_CRC_TABLE_LENGTH * sizeof(bc_entry_t);
	for (bc_order = 0; PAGE_SIZE << bc_order < length; bc_order++) ;
	if (!(bc_table = (bc_entry_t *) __get_free_pages(GFP_KERNEL, bc_order))) {
		cmn_err(CE_PANIC, "%s: Cannot allocated bc_table", __FUNCTION__);
		goto bc_failed;
	}
	printd(("%s: allocated BC table size %u kernel pages\n", DRV_NAME, 1 << bc_order));
	length = SDT_TX_TABLE_LENGTH * sizeof(tx_entry_t);
	for (tx_order = 0; PAGE_SIZE << tx_order < length; tx_order++) ;
	if (!(tx_table = (tx_entry_t *) __get_free_pages(GFP_KERNEL, tx_order))) {
		cmn_err(CE_PANIC, "%s: Cannot allocated tx_table", __FUNCTION__);
		goto tx_failed;
	}
	printd(("%s: allocated Tx table size %u kernel pages\n", DRV_NAME, 1 << tx_order));
	length = 2 * (SDT_RX_TABLE_LENGTH * sizeof(rx_entry_t));
	for (rx_order = 0; PAGE_SIZE << rx_order < length; rx_order++) ;
	if (!(rx_table = (rx_entry_t *) __get_free_pages(GFP_KERNEL, rx_order))) {
		cmn_err(CE_PANIC, "%s: Cannot allocated rx_table", __FUNCTION__);
		goto rx_failed;
	}
	printd(("%s: allocated Rx table size %u kernel pages\n", DRV_NAME, 1 << rx_order));
	rx_table7 = (rx_entry_t *) (((uint8_t *) rx_table) + (PAGE_SIZE << (rx_order - 1)));
	bc_table_generate();
	printd(("%s: generated BC table\n", DRV_NAME));
	tx_table_generate();
	printd(("%s: generated 8-bit Tx table\n", DRV_NAME));
	rx_table_generate8();
	printd(("%s: generated 8-bit Rx table\n", DRV_NAME));
	rx_table_generate7();
	printd(("%s: generated 7-bit Rx table\n", DRV_NAME));
	return (0);
      rx_failed:
	free_pages((unsigned long) tx_table, xchg(&tx_order, 0));
	tx_order = 0;
      tx_failed:
	free_pages((unsigned long) bc_table, xchg(&bc_order, 0));
	bc_order = 0;
      bc_failed:
	return (-ENOMEM);
}

/** xp_free_tables: - Table free.
  *
  * There are 4 mostly read-only tables that were allocated and initialized for SOFT-HDLC operation.
  * These tables are freed here.
  */
noinline int
xp_free_tables(void)
{
	free_pages((unsigned long) bc_table, bc_order);
	printd(("%s: freed BC table kernel pages\n", DRV_NAME));
	free_pages((unsigned long) tx_table, tx_order);
	printd(("%s: freed Tx table kernel pages\n", DRV_NAME));
	free_pages((unsigned long) rx_table, rx_order);
	printd(("%s: freed Rx table kernel pages\n", DRV_NAME));
	return (0);
}

/*
 *  =========================================================================
 *
 *  EVENTS From Above
 *
 *  =========================================================================
 */

/*
 *  M_DATA
 *  -----------------------------------
 */

/** xp_send_data: - process M_DATA message
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the M_DATA message
  *
  * Note that we discard M_DATA messages when we are in the wrong state because the state may have
  * changed with data in queue.  In reality, we should always send a M_FLUSH message upstream in
  * such a case before changing the state, so it still represents a software error.
  */
static inline fastcall __hot_write int
xp_send_data(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;
	int err = 0;

	if (unlikely((ch = xp->ch) == NULL))
		goto discard;
	if (unlikely(xp_get_state(xp) != LMI_ENABLED))
		goto eproto;
	spin_lock_bh(&ch->lock);
	{
		if (ch->sl.statem.lsc_state != SL_STATE_POWER_OFF) {
			/* SL mode */
			if ((err = sl_lsc_pdu(ch, q, mp)) == 0)
				mp = NULL;
		} else if (ch->sdt.statem.daedt_state != SDT_STATE_IDLE) {
			/* SDT mode */
			if (ch->sdt.tb.q_count > 1024)
				err = -EBUSY;
			else {
				/* FIXME: duplicate messages to the SDT xray chain here... Also, needs to be
				   marked as the transmission direction, so add an SDT_DAEDT_TRANSMISSION_REQ 
				   control block. */
				bufq_queue(&ch->sdt.tb, mp);
				mp = NULL;
			}
		} else if (ch->sdl.statem.tx_state != SDL_STATE_IDLE) {
			/* SDL mode */
			if (ch->sdl.tb.q_count > 64)
				err = -EBUSY;
			else {
				/* FIXME: duplicate messages to the SDL xray chain here... Also, needs to be
				   marked as the transmission direction, so add an SDL_BITS_FOR_TRANSMISSION
				   control block. */
				bufq_queue(&ch->sdl.tb, mp);
				mp = NULL;
			}
		} else {
			/* discard when not active */
		}
	}
	spin_unlock_bh(&ch->lock);
	if (mp && !err)
		freemsg(mp);
	return (err);
      eproto:
	return m_error(xp, q, mp, EPROTO);
      discard:
	freemsg(mp);
	return (0);
}

/** sl_pdu_req: - process SL_PDU_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_PDU_REQ primitive
  */
static inline fastcall __hot_write int
sl_pdu_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;
	int err = 1;

	if (likely((ch = xp->ch) != NULL)) {
		spin_lock_bh(&ch->lock);
		{
			err = sl_lsc_pdu(ch, q, mp);
		}
		spin_unlock_bh(&ch->lock);
	}
	if (unlikely(err == 1)) {
		freemsg(mp);
		return (0);
	}
	return (err);
}

/** sl_emergency_req: - process SL_EMERGENCY_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_EMERGENCY_REQ primitive
  *
  * Emergency can be set or cleared on a signalling link at any time that the interface is enabled:
  * even before starting the signalling link.
  */
noinline fastcall __unlikely int
sl_emergency_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_emergency(ch);
	freemsg(mp);
	return (0);
}

/** sl_emergency_ceases_req: - process SL_EMERGENCY_CEASES_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_EMERGENCY_CEASES_REQ primitive
  *
  * Emergency can be set or cleared on a signalling link at any time that the interface is enabled:
  * even before starting the signalling link.
  */
noinline fastcall __unlikely int
sl_emergency_ceases_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_emergency_ceases(ch);
	freemsg(mp);
	return (0);
}

/** sl_start_req: - process SL_START_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_START_REQ primitive
  *
  * SL_START_REQ can be issued on an interface at any time that it is enabled.
  */
noinline fastcall __unlikely int
sl_start_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_start(ch);
	freemsg(mp);
	return (0);
}

/** sl_stop_req: - process SL_STOP_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_STOP_REQ primitive
  *
  * SL_STOP_REQ can be issued on an interface at any time that it is enabled.
  */
noinline fastcall __unlikely int
sl_stop_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_stop(ch);
	freemsg(mp);
	return (0);
}

/** sl_retrieve_bsnt_req: - process SL_RETRIEVE_BSNT_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_RETRIEVE_BSNT_REQ primitive
  *
  * SL_RETRIEVE_BSNT_REQ can be issued on an interface at any time that it is enabled.
  */
noinline fastcall __unlikely int
sl_retrieve_bsnt_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_retrieve_bsnt(ch, q, mp);
	return (0);
}

/** sl_retrieval_request_and_fsnc_req: - process SL_RETRIEVAL_REQUEST_AND_FSNC_REQ
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_RETRIEVAL_REQUEST_AND_FSNC_REQ primitive
  *
  * SL_RETRIEVAL_REQUEST_AND_FSNC_REQ can be issued on an interface at any time that it is enabled.
  */
noinline fastcall __unlikely int
sl_retrieval_request_and_fsnc_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_retrieval_req_and_fsnc_t *p = ((typeof(p)) mp->b_rptr);

	if (unlikely(!MBLKIN(mp, 0, sizeof(*p))))
		return (-EMSGSIZE);
	return sl_lsc_retrieval_request_and_fsnc(ch, q, mp, p->sl_fsnc);
}

/** sl_clear_buffers_req: - process SL_CLEAR_BUFFERS_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_CLEAR_BUFFERS_REQ primitive
  *
  * SL_CLEAR_BUFFERS_REQ can be issued on an interface at any time that it is enabled.
  */
noinline fastcall __unlikely int
sl_clear_buffers_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_clear_buffers(ch, q, mp);
	return (0);
}

/** sl_clear_rtb_req: - process SL_CLEAR_RTB_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_CLEAR_RTB_REQ primitive
  *
  * SL_CLEAR_RTB_REQ can be issued on an interface at any time that it is enabled.
  */
noinline fastcall __unlikely int
sl_clear_rtb_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_clear_rtb(ch, q, mp);
	return (0);
}

/** sl_continue_req: - process SL_CONTINUE_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_CONTINUE_REQ primitive
  *
  * SL_CONTINUE_REQ can be issued on an interface any time that the interface is enabled.
  */
noinline fastcall __unlikely int
sl_continue_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_continue(ch);
	freemsg(mp);
	return (0);
}

/** sl_local_processor_outage_req: - process SL_LOCAL_PROCESSOR_OUTAGE primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_LOCAL_PROCESSOR_OUTAGE_REQ primitive
  *
  * SL_LOCAL_PROCESSOR_OUTAGE_REQ can be issued on an interface any time that the interface is
  * enabled.  For a signalling link in the out-of-service or initial-alignment state, this primitive
  * has no effect other than setting the local processor outage flag.
  *
  * The actions taken by this primitive should also be taken when the interface closes while the
  * interace is enabled.
  *
  * Note: this signal is largely synthetic and is provided to support M2UA where the connection to
  * level 3 exists remote to the signalling link implementation.
  */
noinline fastcall __unlikely int
sl_local_processor_outage_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_local_processor_outage(ch);
	freemsg(mp);
	return (0);
}

/** sl_resume_req: - process SL_RESUME_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_RESUME_REQ primitive
  *
  * SL_RESUME_REQ can be issued on an interface any time that the interface is enabled.  When the
  * link is not running, this has no effect other than to reset local processor outage condition.
  * When the link is in service without a local processor outage condition, this primitive has no
  * effect.
  *
  * To support detaching from signalling links that are left operating, an SL_RESUME_REQ should be
  * considered as the same as SL_START_REQ when the LSC state is out-of-service.  Also, the
  * SL_START_REQ should be considered as the same as SL_RESUME_REQ when the LSC state is
  * processor-outage with a local-processor-outage marked.
  */
noinline fastcall __unlikely int
sl_resume_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	return sl_lsc_resume(ch, q, mp);
}

/** sl_congestion_discard_req: - process SL_CONGESTION_DISCARD_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_CONGESTION_DISCARD_REQ primitive
  *
  * SL_CONGESTION_DISCARD_REQ can be issued on an interface any time that the interface is enabled.
  * When the link is not running, this has no effect other than to set the congestion-discard and
  * marking level 3 congestion detected.
  *
  * There are two receive congestion methods: (1) external, where level 3 detects congestion; and,
  * (2) internal, where level 2 automatically detects congestion.  SL_CONGESTION_DISCARD_REQ marks
  * level 3 congestion as congestion discard and the internal method is no longer used to detect
  * congestion.
  *
  * Note: this signal is largely synthetic and is provided to support M2UA where a buffer exists
  * remote to the signalling link implementation.
  */
noinline fastcall __unlikely int
sl_congestion_discard_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_congestion_discard(ch);
	freemsg(mp);
	return (0);
}

/** sl_congestion_accept_req: - process SL_CONGESTION_ACCEPT_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_CONGESTION_ACCEPT_REQ primitive
  *
  * SL_CONGESTION_ACCEPT_REQ can be issued on an interface any time that the interface is enabled.
  * When the link is not running, this has no effect other than to set the congestion-discard and
  * marking level 3 congestion detected.
  *
  * There are two receive congestion methods: (1) external, where level 3 detects congestion; and,
  * (2) internal, where level 2 automatically detects congestion.  SL_CONGESTION_ACCEPT_REQ marks
  * level 3 congestion as congestion accept and the internal method is no longer used to detect
  * congestion.
  *
  * Note: this signal is largely synthetic and is provided to support M2UA where a buffer exists
  * remote to the signalling link implementation.
  */
noinline fastcall __unlikely int
sl_congestion_accept_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_congestion_accept(ch);
	freemsg(mp);
	return (0);
}

/** sl_no_congestion_req: - process SL_NO_CONGESTION_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_NO_CONGESTION_REQ primitive
  *
  * SL_NO_CONGESTION_REQ can be issued on an interface any time that the interface is enabled.  When
  * the link is not running, this has no effect other than to clear congestion-discard and
  * congestion-accept and clearing level 3 congestion detected.
  *
  * There are two receive congestion methods: (1) external, where level 3 detects congestion; and,
  * (2) internal, where level 2 automatically detects congestion.  SL_NO_CONGESTION_REQ marks level
  * 3 congestion as no congestion and the internal method is used to detect congestion.
  *
  * Note: this signal is largely synthetic and is provided to support M2UA where a buffer exists
  * remote to the signalling link implementation.
  */
noinline fastcall __unlikely int
sl_no_congestion_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_no_congestion(ch);
	freemsg(mp);
	return (0);
}

/** sl_power_on_req: - process SL_POWER_ON_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SL_POWER_ON_REQ primitive
  *
  * SL_POWER_ON_REQ can be issued on an interface any time that the interface is enabled.  Any time
  * that the LSC state is other than SL_STATE_POWER_OFF, this primitive has no effect on the state
  * machine.
  */
noinline fastcall __unlikely int
sl_power_on_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_lsc_power_on(ch);
	freemsg(mp);
	return (0);
}

#if 0
/*
 *  SL_OPTMGMT_REQ
 *  -----------------------------------
 */
noinline fastcall __unlikely int
sl_optmgmt_req(queue_t *q, mblk_t *mp)
{
}

/*
 *  SL_NOTIFY_REQ
 *  -----------------------------------
 */
noinline fastcall __unlikely int
sl_notify_req(queue_t *q, mblk_t *mp)
{
}
#endif

/*
 *  -------------------------------------------------------------------------
 *
 *  SDT Primitives
 *
 *  -------------------------------------------------------------------------
 */

/** sdt_daedt_transmission_req: - process SDT_DAEDT_TRANSMISSION_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_DAEDT_TRANSMISSION_REQ primitive
  *
  * Non-preferred way of sending frames.  One should just send M_DATA blocks.  We used to just strip
  * the redundant M_PROTO and put the M_DATA on the queue, but now we handle it here.
  */
noinline fastcall __hot_write int
sdt_daedt_transmission_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;
	int err = 0;

	if (likely((ch = xp->ch) != NULL)) {
		spin_lock_bh(&ch->lock);
		{
			if (likely(ch->sdt.statem.daedt_state != SDT_STATE_IDLE)) {
				if (unlikely(ch->sdt.tb.q_count > 1024))
					err = -EBUSY;
				else {
					mblk_t *dp;

					/* FIXME: duplicate messages to the SDT xray chain here... Also,
					   needs to be marked as the transmission direction, so just keep the 
					   M_PROTO block. */
					if (likely((dp = XCHG(&mp->b_cont, NULL)) != NULL))
						bufq_queue(&ch->sdt.tb, dp);
				}
			}
		}
		spin_unlock_bh(&ch->lock);
	}
	if (likely(!err))
		freemsg(mp);
	return (err);
}

/** sdt_daedt_start_req: - process SDT_DAEDT_START_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_DAEDT_START_REQ primitive
  */
noinline fastcall __unlikely int
sdt_daedt_start_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_daedt_start(ch);
	freemsg(mp);
	return (0);
}

/** sdt_daedr_start_req: - process SDT_DAEDR_START_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_DAEDR_START_REQ primitive
  */
noinline fastcall __unlikely int
sdt_daedr_start_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_daedr_start(ch);
	freemsg(mp);
	return (0);
}

/** sdt_aerm_start_req: - process SDT_AERM_START_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_AERM_START_REQ primitive
  */
noinline fastcall __unlikely int
sdt_aerm_start_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_aerm_start(ch);
	freemsg(mp);
	return (0);
}

/** sdt_aerm_stop_req: - process SDT_AERM_STOP_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_AERM_STOP_REQ primitive
  */
noinline fastcall __unlikely int
sdt_aerm_stop_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_aerm_stop(ch);
	freemsg(mp);
	return (0);
}

/** sdt_aerm_set_ti_to_tin_req: - process SDT_AERM_SET_TI_TO_TIN_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_AERM_SET_TI_TO_TIN_REQ primitive
  */
noinline fastcall __unlikely int
sdt_aerm_set_ti_to_tin_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_aerm_set_ti_to_tin(ch);
	freemsg(mp);
	return (0);
}

/** sdt_aerm_set_ti_to_tie_req: - process SDT_AERM_SET_TI_TO_TIE_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_AERM_SET_TI_TO_TIE_REQ primitive
  */
noinline fastcall __unlikely int
sdt_aerm_set_ti_to_tie_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_aerm_set_ti_to_tie(ch);
	freemsg(mp);
	return (0);
}

/** sdt_suerm_start_req: - process SDT_SUERM_START_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_SUERM_START_REQ primitive
  */
noinline fastcall __unlikely int
sdt_suerm_start_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_suerm_start(ch);
	freemsg(mp);
	return (0);
}

/** sdt_suerm_stop_req: - process SDT_SUERM_STOP_REQ primitive
  * @ch: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDT_SUERM_STOP_REQ primitive
  */
noinline fastcall __unlikely int
sdt_suerm_stop_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sl_suerm_stop(ch);
	freemsg(mp);
	return (0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  SDL Primitives
 *
 *  -------------------------------------------------------------------------
 */

/** sdl_bits_for_transmission_req: - process SDL_BITS_FOR_TRANSMISSION_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDL_BITS_FOR_TRANSMISSION_REQ primitive
  *
  * Non-preferred method.  Normally one should just send M_DATA blocks.  We used to just strip off
  * the redundant M_PROTO and put it back on the queue, but now we handle it here.
  */
noinline fastcall __hot_write int
sdl_bits_for_transmission_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;
	int err = 0;

	if (likely((ch = xp->ch) != NULL)) {
		spin_lock_bh(&ch->lock);
		{
			if (likely(ch->sdl.statem.tx_state != SDL_STATE_IDLE)) {
				if (unlikely(ch->sdl.tb.q_count > 64))
					err = -EBUSY;
				else {
					mblk_t *dp;

					/* FIXME: duplicate messages to the SDL xray chain here... Also,
					   needs to be marked as the transmission direction, so just keep the 
					   M_PROTO block. */
					if (likely((dp = XCHG(&mp->b_cont, NULL)) != NULL))
						bufq_queue(&ch->sdl.tb, dp);
				}
			}
		}
		spin_unlock_bh(&ch->lock);
	}
	if (likely(!err))
		freemsg(mp);
	return (err);
}

/** sdl_connect_req: - process SDL_CONNECT_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDL_CONNECT_REQ primitive
  */
noinline fastcall __unlikely int
sdl_connect_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sdl_connect_req_t *p = (typeof(p)) mp->b_rptr;

	if (unlikely(!MBLKIN(mp, 0, sizeof(*p))))
		return (-EMSGSIZE);

	if (p->sdl_flags & SDL_RX_DIRECTION) {
		LOGNO(ch2sid(ch), "Enabling Rx");
		ch->sdl.config.ifflags |= SDL_IF_RX_RUNNING;
		ch->sdl.statem.rx_state = SDL_STATE_IN_SERVICE;
	}
	if (p->sdl_flags & SDL_TX_DIRECTION) {
		LOGNO(ch2sid(ch), "Enabling Tx");
		ch->sdl.config.ifflags |= SDL_IF_TX_RUNNING;
		ch->sdl.statem.tx_state = SDL_STATE_IN_SERVICE;
	}
	if ((ch->sdl.config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))) {
		LOGNO(ch2sid(ch), "Marking interface up");
		ch->sdl.config.ifflags |= SDL_IF_UP;
	}
	freemsg(mp);
	return (0);
}

/** sdl_disconnect_req: - process SDL_DISCONNECT_REQ primitive
  * @ch: channel structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDL_DISCONNECT_REQ primitive
  */
noinline fastcall __unlikely int
sdl_disconnect_req(struct ch *ch, queue_t *q, mblk_t *mp)
{
	sdl_disconnect_req_t *p = (typeof(p)) mp->b_rptr;

	if (unlikely(!MBLKIN(mp, 0, sizeof(*p))))
		return (-EMSGSIZE);

	if (p->sdl_flags & SDL_RX_DIRECTION) {
		ch->sdl.config.ifflags &= ~SDL_IF_RX_RUNNING;
		ch->sdl.statem.rx_state = SDL_STATE_IDLE;
		LOGNO(ch2sid(ch), "Disabling Rx");
	}
	if (p->sdl_flags & SDL_TX_DIRECTION) {
		ch->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING;
		ch->sdl.statem.tx_state = SDL_STATE_IDLE;
		LOGNO(ch2sid(ch), "Disabling Tx");
	}
	if (!(ch->sdl.config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))) {
		LOGNO(ch2sid(ch), "Marking interface down");
		ch->sdl.config.ifflags &= ~SDL_IF_UP;
	}
	freemsg(mp);
	return (0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  LMI primitives
 *
 *  -------------------------------------------------------------------------
 */

/** lmi_info_req: - process LMI_INFO_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_INFO_REQ primitive
  */
noinline fastcall __unlikely int
lmi_info_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	uint16_t ppa;
	caddr_t ppa_ptr = NULL;
	size_t ppa_len = 0;

	switch (xp_get_state(xp)) {
		struct ch *ch;

	case LMI_ENABLED:
	case LMI_DISABLED:
		if ((ch = xp->ch)) {
			ppa = ch->ppa;
			ppa_ptr = (caddr_t) &ppa;
			ppa_len = sizeof(ppa);
		}
		break;
	}
	return lmi_info_ack(xp, q, mp, ppa_ptr, ppa_len);
}

/** lmi_attach_req: - process LMI_ATTACH_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_ATTACH_REQ primitive
  *
  * LMI_ATTACH_REQ results in attaching the xp structure to the appropriate ch structure.
  *
  * We used to bind the xp structure to the ch structure when performing an attach.  However, this
  * meant that one could not open a signalling link, attach to a ppa, perform some ioctls, and then
  * detach and close.  What we do now is a semi-attachment: we link the xp structure to the ch
  * structure but not vise versa.  The difference is that the ch structure can be attached to a
  * different xp structure in an active connection.
  */
noinline fastcall __unlikely int
lmi_attach_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	lmi_attach_req_t *p = ((typeof(p)) mp->b_rptr);
	uint16_t ppa;
	int card, span, chan;

	if (!MBLKIN(mp, 0, sizeof(*p)))
		goto badprim;
	if (xp_get_state(xp) != LMI_UNATTACHED)
		goto outstate;
	if (!MBLKIN(mp, p->lmi_ppa_offset, p->lmi_ppa_length))
		goto badppa;
	if (p->lmi_ppa_length != sizeof(ppa))
		goto badppa;
	xp_set_state(xp, LMI_ATTACH_PENDING);
	ppa = *(typeof(ppa) *) (mp->b_rptr + p->lmi_ppa_offset);
	read_lock(&xp_core_lock);
	{
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;

		/* check card */
		card = (ppa >> 12) & 0x0f;
		if (card < 0 || card > X400_CARDS - 1) {
			read_unlock(&xp_core_lock);
			goto badcard;
		}
		if (!(cd = x400p_cards[card])) {
			read_unlock(&xp_core_lock);
			goto nocard;
		}
		/* check span */
		span = (ppa >> 8) & 0x0f;
		if (span < 0 || span > cd->ports - 1) {
			read_unlock(&xp_core_lock);
			goto badspan;
		}
		if (!(sp = cd->spans[span])) {
			read_unlock(&xp_core_lock);
			goto nospan;
		}
#ifdef DEBUG
		if (sp->config.ifgtype != SDL_GTYPE_E1 && sp->config.ifgtype != SDL_GTYPE_T1
		    && sp->config.ifgtype != SDL_GTYPE_J1) {
			read_unlock(&xp_core_lock);
			swerr();
			goto efault;
		}
#endif
		/* check chan */
		chan = (ppa >> 0) & 0xff;
		switch (sp->config.ifgtype) {
		case SDL_GTYPE_E1:
			if (chan < 0 || chan > 31) {
				read_unlock(&xp_core_lock);
				goto badchan;
			}
			break;
		case SDL_GTYPE_T1:
		case SDL_GTYPE_J1:
			if (chan < 0 || chan > 24) {
				read_unlock(&xp_core_lock);
				goto badchan;
			}
			break;
		default:
			read_unlock(&xp_core_lock);
			goto badppa;
		}
		if (!(ch = sp->chans[chan])) {
			read_unlock(&xp_core_lock);
			goto nochan;
		}
#if 0
		if ((ch = sp->chans[0]) && ch->xp) {
			read_unlock(&xp_core_lock);
			goto chanbusy;
		}
		if (chan == 0) {
			int c;

			switch (sp->config.ifgtype) {
			case SDL_GTYPE_E1:
				for (c = 1; c < 32; c++) {
					if (!(ch = sp->chans[c]))
						continue;
					if (ch->xp)
						goto chanbusy;
				}
				break;
			case SDL_GTYPE_T1:
			case SDL_GTYPE_J1:
				for (c = 1; c < 25; c++) {
					if (!(ch = sp->chans[c]))
						continue;
					if (ch->xp)
						goto chanbusy;
				}
				break;
			default:
				goto badppa;
			}
		} else {
			if ((ch = sp->chans[chan]))
				if (ch->xp)
					goto chanbusy;
		}
#endif
		ch = sp->chans[chan];
		xp->card = card;
		xp->span = span;
		xp->chan = chan;
		xp->ch = ch_get(ch);
#if 0
		ch->xp = xp;
#endif
	}
	read_unlock(&xp_core_lock);
	LOGNO(xp2sid(xp), "INFO: attached card %d, span %d, chan %d", card, span, chan);
	lmi_ok_ack(xp, q, mp, LMI_DISABLED, LMI_ATTACH_REQ);
	return (0);
#ifdef DEBUG
      efault:
	return (-EFAULT);
#endif
      outstate:
	LOGNO(xp2sid(xp), "%s: ERROR: interface out of state", __FUNCTION__);
	return (-EPROTO);
      badprim:
	LOGNO(xp2sid(xp), "%s: ERROR: primitive too small = %ld bytes", __FUNCTION__, (long) MBLKSIZE(mp));
	return (-EINVAL);
      badcard:
	LOGNO(xp2sid(xp), "%s: ERROR: invalid card %d", __FUNCTION__, card);
	goto badppa;
      nocard:
	LOGNO(xp2sid(xp), "%s: ERROR: unallocated card %d", __FUNCTION__, card);
	goto badppa;
      badspan:
	LOGNO(xp2sid(xp), "%s: ERROR: invalid span %d", __FUNCTION__, span);
	goto badppa;
      nospan:
	LOGNO(xp2sid(xp), "%s: ERROR: unallocated span %d", __FUNCTION__, span);
	goto badppa;
      badchan:
	LOGNO(xp2sid(xp), "%s: ERROR: invalid chan %d", __FUNCTION__, chan);
	goto badppa;
      nochan:
	LOGNO(xp2sid(xp), "%s: ERROR: unallocated chan %d", __FUNCTION__, chan);
	goto badppa;
#if 0
      chanbusy:
	LOGNO(xp2sid(xp), "%s: ERROR: already in use, chan %d", __FUNCTION__, chan);
	goto badppa;
#endif
      badppa:
	return (-EADDRNOTAVAIL);
}

/** lmi_detach_req: - process LMI_DETACH_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_DETACH_REQ primitive
  *
  * LMI_DETACH_REQ results in detaching the xp structure from the ch structure to which it is
  * attached.
  */
noinline fastcall __unlikely int
lmi_detach_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;
	psw_t flags;

	/* validate detach */
	if (xp_get_state(xp) != LMI_DISABLED)
		goto outstate;
	xp_set_state(xp, LMI_DETACH_PENDING);

	write_lock_irqsave(&xp_core_lock, flags);
	{
		ch = XCHG(&xp->ch, NULL);
#ifdef DEBUG
		if (ch == NULL) {
			write_unlock_irqrestore(&xp_core_lock, flags);
			goto efault;
		}
#endif
		ch_put(ch);
		xp->card = -1;
		xp->span = -1;
		xp->chan = -1;
	}
	write_unlock_irqrestore(&xp_core_lock, flags);
	lmi_ok_ack(xp, q, mp, LMI_UNATTACHED, LMI_DETACH_REQ);
	return (0);
      outstate:
	LOGNO(xp2sid(xp), "%s: ERROR: interface out of state", __FUNCTION__);
	return (-EPROTO);
#ifdef DEBUG
      efault:
	LOGERR(xp2sid(xp), "%s: SWERR: not attached", __FUNCTION__);
	return (-EFAULT);
#endif
}

/** startup_span: - startup span if not already running
  * @sp: span structure pointer
  */
noinline fastcall __unlikely void
startup_span(struct sp *sp)
{
	if (!(sp->config.ifflags & SDL_IF_UP)) {
		/* need to bring up span */
		struct cd *cd = sp->cd;
		psw_t flags = 0;

		spin_lock_irqsave(&cd->lock, flags);
		sp->config.ifflags |= (SDL_IF_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING);
		/* enable interrupts */
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | DINTENA);
		spin_unlock_irqrestore(&cd->lock, flags);
	}
}

/** lmi_enable_req: - process LMI_ENABLE_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_ENABLE_REQ primitive
  *
  * LMI_ENABLE_REQ used to configure the timeslot for operation and start up the span if it was not
  * already started, 
  *
  * We use to bind the xp structure to the ch structure when performing an attach.  However, this
  * meant that one could not open a signalling link, attach to a ppa, perform some ioctls, and then
  * detach and close.  What we do now is a semi-attachment: we link the xp structure to the ch
  * structure but no vise versa.  The ch structure is only linked back to a xp structure on enable.
  */
noinline fastcall __unlikely int
lmi_enable_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;
	struct sp *sp;
	psw_t flags;
	int chan = xp->chan;

	/* validate enable */
	if (xp_get_state(xp) != LMI_DISABLED)
		goto outstate;
	if (!(ch = xp->ch)) {
		ptrace(("%s: ERROR: out of state: no chan pointer\n", DRV_NAME));
		goto outstate;
	}
	if (!(sp = ch->sp)) {
		ptrace(("%s: ERROR: out of state: no span pointer\n", DRV_NAME));
		goto outstate;
	}
	printd(("%s: %p: performing enable\n", DRV_NAME, xp));
	xp_set_state(xp, LMI_ENABLE_PENDING);
	spin_lock_irqsave(&ch->lock, flags);
	{
		if (ch->sdl.config.ifflags & SDL_IF_UP) {
			spin_unlock_irqrestore(&ch->lock, flags);
			ptrace(("%s: ERROR: out of state: device already up\n", DRV_NAME));
			goto outstate;
		}
		if (ch->xp != NULL) {
			spin_unlock_irqrestore(&ch->lock, flags);
			goto chanbusy;
		}
		if (chan == 0) {
			int c;

			for (c = 1; c < 32; c++) {
				if (sp->chans[c] == NULL)
					continue;
				if (sp->chans[c]->xp != NULL) {
					spin_unlock_irqrestore(&ch->lock, flags);
					goto chanbusy;
				}
			}
		} else {
			if (sp->chans[0]->xp != NULL) {
				spin_unlock_irqrestore(&ch->lock, flags);
				goto chanbusy;
			}
		}
		/* commit enable */
		ch->xp = xp;
		ch->sdl.config.ifflags |= SDL_IF_UP;
	}
	spin_unlock_irqrestore(&ch->lock, flags);
	startup_span(sp);
	xp_set_state(xp, LMI_ENABLED);
	lmi_enable_con(xp, q, mp);
	return (0);
      outstate:
	LOGNO(xp2sid(xp), "%s: ERROR: out of state: state = %u", __FUNCTION__, xp_get_state(xp));
	return (-EPROTO);
      chanbusy:
	LOGNO(xp2sid(xp), "%s: ERROR: already in use, chan %d", __FUNCTION__, chan);
	return (-EADDRNOTAVAIL);
}

/** lmi_disable_req: - process LMI_DISABLE_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_DISABLE_REQ primitive
  */
noinline fastcall __unlikely int
lmi_disable_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch = xp->ch;
	struct sp *sp;
	struct cd *cd;

	/* validate disable */
	if (xp_get_state(xp) != LMI_ENABLED)
		goto lmi_outstate;
	xp_set_state(xp, LMI_DISABLE_PENDING);
	/* commit disable */
	if ((sp = ch->sp) && (cd = sp->cd)) {
		psw_t flags = 0;

		spin_lock_irqsave(&cd->lock, flags);
		{
			int chan, boff;
			uchar idle, *base = (uchar *) cd->wbuf + span_to_byte(sp->span);

			switch (cd->config.ifgtype) {
			case SDL_GTYPE_T1:
			case SDL_GTYPE_J1:
				idle = 0xfe;
				for (chan = 1; chan < 32; chan++)
					if (sp->chans[chan] == ch)
						for (boff = 0; boff < X400P_EBUFNO; boff++)
							*(base + (boff << 10) + (xp_t1_chan_map[chan] << 2)) = idle;
				break;
			default:
			case SDL_GTYPE_E1:
				idle = 0xff;
				for (chan = 1; chan < 32; chan++)
					if (sp->chans[chan] == ch)
						for (boff = 0; boff < X400P_EBUFNO; boff++)
							*(base + (boff << 10) + (xp_e1_chan_map[chan] << 2)) = idle;
				break;
			}
			/* stop transmitters and receivers */
			ch->sdl.config.ifflags = 0;
			ch->sdl.statem.tx_state = SDL_STATE_IDLE;
			ch->sdl.statem.rx_state = SDL_STATE_IDLE;
			/* stop daedr and daedt */
			ch->sdt.statem.daedt_state = SDT_STATE_IDLE;
			ch->sdt.statem.daedr_state = SDT_STATE_IDLE;
			/* stop aerm */
			ch->sdt.statem.aerm_state = SDT_STATE_IDLE;
			ch->sdt.statem.Ti = ch->sdt.config.Tin;
			/* stop eim */
			ch->sdt.statem.eim_state = SDT_STATE_IDLE;
			xp_timer_stop(ch, t8);
			/* stop suerm */
			ch->sdt.statem.suerm_state = SDT_STATE_IDLE;
			/* reset transmitter and receiver state */
			if (ch->tx.msg && ch->tx.msg != ch->tx.cmp)
				freemsg(xchg(&ch->tx.msg, NULL));
			if (ch->tx.cmp)
				freemsg(xchg(&ch->tx.cmp, NULL));
			if (ch->rx.msg)
				freemsg(xchg(&ch->rx.msg, NULL));
			if (ch->rx.nxt)
				freemsg(xchg(&ch->rx.nxt, NULL));
			if (ch->rx.cmp)
				freemsg(xchg(&ch->rx.cmp, NULL));
			bzero(&ch->tx, sizeof(ch->tx));
			bzero(&ch->rx, sizeof(ch->tx));
			if (ch->xp == xp)
				ch->xp = NULL;
		}
		spin_unlock_irqrestore(&cd->lock, flags);
	} else
		swerr();
	/* perform disable */
	lmi_disable_con(xp, q, mp);
	return (0);
      lmi_outstate:
	return (-EPROTO);
}

/** lmi_optmgmt_req: - process LMI_OPTMGMT_REQ primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI_OPTMGMT_REQ primitive
  */
noinline fastcall __unlikely int
lmi_optmgmt_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	fixme(("FIXME: must check for options change\n"));
	return (-EOPNOTSUPP);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  DLPI PRIMITIVES
 *
 *  -------------------------------------------------------------------------
 */

/** dl_info_req: - process DL_INFO_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_INFO_REQ primitive
  */
noinline fastcall __unlikely int
dl_info_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	dl_info_req_t *p;

	if (!MBLKIN(mp, 0, sizeof(*p)))
		goto badprim;
	return dl_info_ack(xp, q, mp);
      badprim:
	return (-EMSGSIZE);
}

/** dl_attach_req: - process DL_ATTACH_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_ATTACH_REQ primitive
  *
  *  This is not quite the same as the LMI.  When we attach, we do not do anything more than
  *  validate the PPA.  While LMI binds to the ch structure at this point, we do not bind to the ch
  *  until the DL_BIND_REQ.  That is because there are a couple of ways of specifying that
  *  monitoring is to be performed (dl_sap == 0, dl_service_mode of DL_HP_RAWDLS with a
  *  dl_max_conind of 1), etc.
  */
noinline fastcall __unlikely int
dl_attach_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	dl_attach_req_t *p;

	prefetchw(xp);
	if (!MBLKIN(mp, 0, sizeof(*p)))
		goto badprim;
	p = (typeof(p)) mp->b_rptr;
	if (dl_get_state(xp) != DL_UNATTACHED)
		goto outstate;
	dl_set_state(xp, DL_ATTACH_PENDING);
	xp->ppa = p->dl_ppa;
	if (xp->ppa == 0) {
		/* Just want to attach to everything, that's ok.  This is a global attach. */
		xp->card = -1;
		xp->span = -1;
		xp->chan = -1;
	} else {
		/* check card */
		if ((xp->card = (xp->ppa >> 12) & 0x0f) == 0x0f) {
			/* want to attach to all cards, same as ppa == 0 */
			xp->ppa = 0;
			xp->card = -1;
			xp->span = -1;
			xp->chan = -1;
		} else {
			struct cd *cd;

			if (0 > xp->card || xp->card > X400_CARDS - 1)
				goto badppa;
			if (!(cd = x400p_cards[xp->card]))
				goto badppa;
			/* check span */
			if ((xp->span = (xp->ppa >> 8) & 0x0f) == 0x0f) {
				/* want to attach to all spans on this card */
				xp->span = -1;
				xp->chan = -1;
			} else {
				struct sp *sp;
				struct ch *ch;

				if (0 > xp->span || xp->span > cd->ports - 1)
					goto badppa;
				if (!(sp = cd->spans[xp->span]))
					goto badppa;
				/* check chan */
				if ((xp->chan = (xp->ppa >> 0) & 0xff) == 0xff) {
					/* want all channels in this span */
					xp->chan = -1;
				} else {
					switch (sp->config.ifgtype) {
					case SDL_GTYPE_E1:
						if (xp->chan < 0 || xp->chan > 31)
							goto badppa;
						break;
					case SDL_GTYPE_T1:
					case SDL_GTYPE_J1:
						if (xp->chan < 0 || xp->chan > 24)
							goto badppa;
						break;
					default:
						goto badppa;
					}
					if (!(ch = sp->chans[xp->chan]))
						goto badppa;
				}
			}
		}
	}
	dl_set_state(xp, DL_ATTACH_PENDING);
	dl_ok_ack(xp, q, mp, DL_UNBOUND, DL_ATTACH_REQ);
	return (0);
      badprim:
	return (-EMSGSIZE);
      outstate:
	return (-EPROTO);
      badppa:
	return (-EADDRNOTAVAIL);
}

/** dl_detach_req: - process DL_DETACH_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_DETACH_REQ primitive
  *
  * This is largely a state transition: when we are attached (but unbound) to an interface, we
  * simply have the card, span, chan and slot numbers set to what was a valid interface at the time
  * of the attach.  (Because, in general, interfaces do not disappear while the driver is loaded, an
  * interface, once validated, is persistently valid.)
  */
noinline fastcall __unlikely int
dl_detach_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	dl_detach_req_t *p;

	prefetchw(xp);
	if (!MBLKIN(mp, 0, sizeof(*p)))
		goto badprim;
	p = (typeof(p)) mp->b_rptr;
	if (dl_get_state(xp) != DL_UNBOUND)
		goto outstate;
	dl_set_state(xp, DL_DETACH_PENDING);
	xp->card = -1;
	xp->span = -1;
	xp->chan = -1;
	dl_ok_ack(xp, q, mp, DL_UNATTACHED, DL_DETACH_REQ);
	return (0);
      outstate:
	return (-EPROTO);
      badprim:
	return (-EMSGSIZE);
}

/** dl_bind_req: - process DL_BIND_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_BIND_REQ primitive
  *
  * This is where we actually bind the xp structure to the appropriate ch structure (if any).
  */
noinline fastcall __unlikely int
dl_bind_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	dl_bind_req_t *p;
	struct cd *cd = NULL;
	struct sp *sp = NULL;
	struct ch *ch = NULL;
	int card, span;
	ulong reason = 0;
	ulong errno = 0;
	psw_t flags;

	prefetchw(xp);
	if (!MBLKIN(mp, 0, sizeof(*p)))
		goto badprim;
	p = (typeof(p)) mp->b_rptr;
	if (dl_get_state(xp) != DL_IDLE)
		goto outstate;
	dl_set_state(xp, DL_BIND_PENDING);
	/* the layout of these structures is static so no locks are required */
	xp->monitor = XP_MONITOR_NONE;
	if (xp->card == -1) {
		/* driver level bind - always monitoring */
		xp->monitor = XP_MONITOR_GLOB;
		goto monitoring;
	}
	cd = x400p_cards[xp->card];
	if (!cd)
		/* card structure disappeared */
		goto badbad;
	if (xp->span == -1) {
		/* card level bind - always monitoring */
		xp->monitor = XP_MONITOR_CARD;
		goto monitoring;
	}
	sp = cd->spans[xp->span];
	if (!sp)
		/* span structure disappeared */
		goto badbad;
	if (xp->chan == -1) {
		/* span level bind - always monitoring */
		xp->monitor = XP_MONITOR_SPAN;
		goto monitoring;
	}
	ch = sp->chans[xp->chan];
	if (!ch)
		/* chan structure disappeared */
		goto badbad;
      monitoring:
	/* we are attached to a timeslot and are not necessarily monitoring, need some more info from the
	   DL_BIND_REQ to determine what to do. */
	switch (p->dl_service_mode) {
#ifdef _HPUX_SOURCE
	case DL_HP_RAWDLS:
		if (p->dl_max_conind == 1) {
			/* this is how HP does it */
			if (xp->monitor == XP_MONITOR_NONE)
				xp->monitor = XP_MONITOR_CHAN;
		}
		break;
#endif				/* _HPUX_SOURCE */
	case DL_CLDLS:
		/* When it asks for connection-less data link service on this interface, always assume that
		   the application wants monitoring at the chan level. */
		if (xp->monitor == XP_MONITOR_NONE)
			xp->monitor = XP_MONITOR_CHAN;
		break;
	case DL_CODLS:
		/* When it asks for connection-oriented data link service on this interface, always assume
		   that the application wants active connection at the chan level.  If a monitoring PPA was
		   attached, this is an error. */
		if (xp->monitor != XP_MONITOR_NONE)
			goto badserv;
		break;
	case DL_ACLDLS:
	default:
		goto badserv;
	}
	switch ((xp->level = p->dl_sap)) {
	case XP_LEVEL_MON_SDT:	/* base SDT monitoring SAP */
	case XP_LEVEL_MON_SL:	/* base SL monitoring SAP */
		if (xp->monitor == XP_MONITOR_NONE)
			xp->monitor = XP_MONITOR_CHAN;
		break;
	case XP_LEVEL_ACT_SDT:	/* SDT non-monitoring SAP */
	case XP_LEVEL_ACT_SL:	/* SL non-monitoring SAP */
	case XP_LEVEL_ACT_SDL:	/* SDL non-monitoring SAP */
		if (xp->monitor != XP_MONITOR_NONE)
			goto badsap;
		break;
	case XP_LEVEL_MON_SDL:	/* base SDL monitoring SAP */
	default:
		goto badsap;
	}
	switch (xp->monitor) {
		int chan;

	case XP_MONITOR_NONE:	/* not monitoring, active link */
		/* we are doing what lmi_attach and lmi_enable would do */
		if (sp->config.iftxlevel < 8) {
			spin_lock_bh(&ch->lock);
			if (ch->xp == NULL) {
				ch->xp = xp;
				xp->ch = ch_get(ch);
				ch->sdl.statem.rx_state = SDL_STATE_IDLE;
				ch->sdl.statem.tx_state = SDL_STATE_IDLE;
				ch->sdl.config.ifflags = 0;
				ch->sdt.statem.daedr_state = SDT_STATE_IDLE;
				ch->sdt.statem.daedt_state = SDT_STATE_IDLE;
			} else {
				reason = DL_BUSY;
				errno = EADDRNOTAVAIL;
			}
			spin_unlock_bh(&ch->lock);
			if (reason != 0)
				goto addrnotavail;
#if 0
			/* FIXME FIXME FIXME: we need to convert timers back to being independent of a
			   STREAMS queue.  Just use regular linux timers. */
			/* remember to free timers on unbind */
			if (xp_alloc_timers(ch, xp->oq)) {
				spin_lock_bh(&ch->lock);
				ch->xp = NULL;
				ch_put(xchg(&xp->ch, NULL));
				spin_unlock_bh(&ch->lock);
				reason = DL_SYSERR;
				reason = ENOMEM;
			}
#endif
		}
		if (reason != 0)
			goto addrnotavail;
		ch->sdl.config.ifflags |= SDL_IF_UP;
		startup_span(sp);
		break;
	case XP_MONITOR_GLOB:	/* monitoring at the driver level */
		switch (xp->level) {
		case XP_LEVEL_MON_SDT:	/* monitoring SDT */
			xp->xray.flags = XP_XRAY_SDT_GLOB;
			break;
		case XP_LEVEL_MON_SL:	/* monitoring SL */
			xp->xray.flags = XP_XRAY_SL_GLOB;
			break;
		case XP_LEVEL_MON_SDL:	/* monitoring SDL */
			xp->xray.flags = XP_XRAY_SDL_GLOB;
			break;
		}
		read_lock_irqsave(&x400p_lock, flags);
		if ((xp->xray.next = x400p_xrays))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &x400p_xrays;
		x400p_xrays = xp;
		read_unlock_irqrestore(&x400p_lock, flags);
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++) {
				int chan;

				if (!(sp = cd->spans[span]))
					continue;
				if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
					startup_span(sp);
				for (chan = 0; chan < 32; chan++) {
					if (!(ch = sp->chans[chan]))
						continue;
					spin_lock_bh(&ch->lock);
					ch->xray.flags |= xp->xray.flags;
					if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
						sl_daedt_start(ch);
						sl_daedr_start(ch);
					}
					spin_unlock_bh(&ch->lock);
				}
			}
		}
		break;
	case XP_MONITOR_CARD:	/* monitoring at the card level */
		switch (xp->level) {
		case XP_LEVEL_MON_SDT:	/* monitoring SDT */
			xp->xray.flags = XP_XRAY_SDT_CARD;
			break;
		case XP_LEVEL_MON_SL:	/* monitoring SL */
			xp->xray.flags = XP_XRAY_SL_CARD;
			break;
		case XP_LEVEL_MON_SDL:	/* monitoring SDL */
			xp->xray.flags = XP_XRAY_SDL_CARD;
			break;
		}
		spin_lock_irqsave(&cd->lock, flags);
		if ((xp->xray.next = cd->xray))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &cd->xray;
		cd->xray = xp;
		spin_unlock_irqrestore(&cd->lock, flags);
		for (span = 0; span < 4; span++) {
			int chan;

			if (!(sp = cd->spans[span]))
				continue;
			for (chan = 0; chan < 32; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				spin_lock_bh(&ch->lock);
				ch->xray.flags |= xp->xray.flags;
				if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
					sl_daedt_start(ch);
					sl_daedr_start(ch);
				}
				spin_unlock_bh(&ch->lock);
			}
			if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
				startup_span(sp);
		}
		break;
	case XP_MONITOR_SPAN:	/* monitoring at the span level */
		switch (xp->level) {
		case XP_LEVEL_MON_SDT:	/* monitoring SDT */
			xp->xray.flags = XP_XRAY_SDT_SPAN;
			break;
		case XP_LEVEL_MON_SL:	/* monitoring SL */
			xp->xray.flags = XP_XRAY_SL_SPAN;
			break;
		case XP_LEVEL_MON_SDL:	/* monitoring SDL */
			xp->xray.flags = XP_XRAY_SDL_SPAN;
			break;
		}
		spin_lock_irqsave(&sp->lock, flags);
		if ((xp->xray.next = sp->xray))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &sp->xray;
		sp->xray = xp;
		spin_unlock_irqrestore(&sp->lock, flags);
		for (chan = 0; chan < 32; chan++) {
			if (!(ch = sp->chans[chan]))
				continue;
			spin_lock_bh(&ch->lock);
			ch->xray.flags |= xp->xray.flags;
			if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
				sl_daedt_start(ch);
				sl_daedr_start(ch);
			}
			spin_unlock_bh(&ch->lock);
		}
		if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
			startup_span(sp);
		break;
	case XP_MONITOR_CHAN:	/* monitoring at the chan level */
		switch (xp->level) {
		case XP_LEVEL_MON_SDT:	/* monitoring SDT */
			xp->xray.flags = XP_XRAY_SDT_CHAN;
			break;
		case XP_LEVEL_MON_SL:	/* monitoring SL */
			xp->xray.flags = XP_XRAY_SL_CHAN;
			break;
		case XP_LEVEL_MON_SDL:	/* monitoring SDL */
			xp->xray.flags = XP_XRAY_SDL_CHAN;
			break;
		}
		spin_lock_bh(&ch->lock);
		if ((xp->xray.next = ch->xray.list))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &ch->xray.list;
		ch->xray.list = xp;
		ch->xray.flags |= xp->xray.flags;
		if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
			sl_daedt_start(ch);
			sl_daedr_start(ch);
		}
		spin_unlock_bh(&ch->lock);
		if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
			startup_span(sp);
		break;
	}
	dl_bind_ack(xp, q, mp, xp->level, xp->monitor ? 0 : 1);
	return (0);
      addrnotavail:
	return (-EADDRNOTAVAIL);
      badprim:
	return (-EMSGSIZE);
      outstate:
	return (-EPROTO);
      badbad:
	return (-EFAULT);
      badserv:
	return (-EPROTONOSUPPORT);
      badsap:
	return (-EAFNOSUPPORT);
}

/** dl_unbind_req: - process DL_UNBIND_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_UNBIND_REQ primitive
  */
noinline fastcall __unlikely int
dl_unbind_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch = xp->ch;
	dl_unbind_req_t *p;
	psw_t flags;

	if (!MBLKIN(mp, 0, sizeof(*p)))
		goto badprim;
	if (dl_get_state(xp) != DL_IDLE || ch == NULL)
		goto outstate;
	dl_set_state(xp, DL_UNBIND_PENDING);
	switch (xp->monitor) {
	case XP_MONITOR_NONE:	/* not monitoring */
		switch (xp->level) {
		case XP_LEVEL_ACT_SL:	/* SL level */
			spin_lock_bh(&ch->lock);
			sl_lsc_stop(ch);
			spin_unlock_bh(&ch->lock);
			break;
		case XP_LEVEL_ACT_SDT:	/* SDT level */
			spin_lock_bh(&ch->lock);
			ch->sdt.statem.daedr_state = SDT_STATE_IDLE;
			ch->sdt.statem.daedt_state = SDT_STATE_IDLE;
			ch->sdl.config.ifflags &= ~SDL_IF_RX_RUNNING;
			ch->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING;
			spin_unlock_bh(&ch->lock);
			break;
		case XP_LEVEL_ACT_SDL:	/* SDL level */
			spin_lock_bh(&ch->lock);
			ch->sdl.config.ifflags &= ~SDL_IF_RX_RUNNING;
			ch->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING;
			spin_unlock_bh(&ch->lock);
			break;
		}
		break;
	case XP_MONITOR_GLOB:	/* monitoring at driver level */
		xp->xray.flags &= ~XP_XRAY_GLOB;
		/* ch will fix their own xray flags */
		read_lock_irqsave(&x400p_lock, flags);
		if (((*xp->xray.prev) = xp->xray.next))
			xp->xray.next->xray.prev = xp->xray.prev;
		xp->xray.next = NULL;
		xp->xray.prev = &xp->xray.next;
		read_unlock_irqrestore(&x400p_lock, flags);
		break;
	case XP_MONITOR_CARD:	/* monitoring at card level */
	{
		struct cd *cd = cd_find(xp->card);

		if (!cd)
			goto efault;
		xp->xray.flags &= ~XP_XRAY_CARD;
		/* ch will fix their own xray flags */
		spin_lock_irqsave(&cd->lock, flags);
		if (((*xp->xray.prev) = xp->xray.next))
			xp->xray.next->xray.prev = xp->xray.prev;
		xp->xray.next = NULL;
		xp->xray.prev = &xp->xray.next;
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	}
	case XP_MONITOR_SPAN:	/* monitoring at span level */
	{
		struct cd *cd = cd_find(xp->card);
		struct sp *sp;

		if (!cd)
			goto efault;
		sp = cd->spans[xp->span];
		if (!sp)
			goto efault;
		xp->xray.flags &= ~XP_XRAY_SPAN;
		/* ch will fix their own xray flags */
		spin_lock_irqsave(&sp->lock, flags);
		if (((*xp->xray.prev) = xp->xray.next))
			xp->xray.next->xray.prev = xp->xray.prev;
		xp->xray.next = NULL;
		xp->xray.prev = &xp->xray.next;
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	}
	case XP_MONITOR_CHAN:	/* monitoring at chan level */
		xp->xray.flags &= ~XP_XRAY_CHAN;
		/* ch will fix their own xray flags */
		spin_lock_bh(&ch->lock);
		if (((*xp->xray.prev) = xp->xray.next))
			xp->xray.next->xray.prev = xp->xray.prev;
		xp->xray.next = NULL;
		xp->xray.prev = &xp->xray.next;
		spin_unlock_bh(&ch->lock);
		break;
	}
	dl_ok_ack(xp, q, mp, DL_UNBOUND, DL_UNBIND_REQ);
	return (0);
      efault:
	return (-EFAULT);
      outstate:
	return (-EPROTO);
      badprim:
	return (-EMSGSIZE);
}

/** dl_subs_bind_req: - process DL_SUBS_BIND_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_SUBS_BIND_REQ primitive
  */
static inline fastcall __unlikely int
dl_subs_bind_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_subs_unbind_req: - process DL_SUBS_UNBIND_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_SUBS_UNBIND_REQ primitive
  */
static inline fastcall __unlikely int
dl_subs_unbind_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	/* can never be in the right state */
	return (-EPROTO);
}

/** dl_unitdata_req: - process DL_UNITDATA_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_UNITDATA_REQ primitive
  */
static inline fastcall __unlikely int
dl_unitdata_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_udqos_req: - process DL_UDQOS_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_UDQOS_REQ primitive
  */
static inline fastcall __unlikely int
dl_udqos_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_connect_req: - process DL_CONNECT_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_CONNECT_REQ primitive
  */
noinline fastcall __unlikely int
dl_connect_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch = xp->ch;

	if (dl_get_state(xp) != DL_IDLE || ch == NULL)
		goto outstate;
	if (xp->monitor)
		return (-EOPNOTSUPP);
	switch (xp->level) {
	case XP_LEVEL_ACT_SDL:	/* SDL connection */
		spin_lock_bh(&ch->lock);
		ch->sdl.config.ifflags |= SDL_IF_TX_RUNNING;
		ch->sdl.statem.tx_state = SDL_STATE_IN_SERVICE;
		ch->sdl.config.ifflags |= SDL_IF_RX_RUNNING;
		ch->sdl.statem.rx_state = SDL_STATE_IN_SERVICE;
		ch->sdl.config.ifflags |= SDL_IF_UP;
		spin_unlock_bh(&ch->lock);
		break;
	case XP_LEVEL_ACT_SDT:	/* SDT connection */
		spin_lock_bh(&ch->lock);
		sl_daedt_start(ch);
		sl_daedr_start(ch);
		spin_unlock_bh(&ch->lock);
		break;
	case XP_LEVEL_ACT_SL:	/* SL connection */
		spin_lock_bh(&ch->lock);
		sl_lsc_start(ch);
		spin_unlock_bh(&ch->lock);
		break;
	}
	return (-EOPNOTSUPP);
      outstate:
	return (-EPROTO);
}

/** dl_connect_res: - process DL_CONNECT_RES primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_CONNECT_RES primitive
  */
static inline fastcall __unlikely int
dl_connect_res(struct xp *xp, queue_t *q, mblk_t *mp)
{
	/* can never be in correct state */
	return (-EPROTO);
}

/** dl_token_req: - process DL_TOKEN_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_TOKEN_REQ primitive
  */
noinline fastcall __unlikely int
dl_token_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	dl_token_ack(xp, q, mp);
	return (0);
}

/** dl_disconnect_req: - process DL_DISCONNECT_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_DISCONNECT_REQ primitive
  */
noinline fastcall __unlikely int
dl_disconnect_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch = xp->ch;

	if (dl_get_state(xp) != DL_DATAXFER || ch == NULL)
		goto outstate;
	if (xp->monitor)
		return (-EOPNOTSUPP);
	switch (xp->level) {
	case XP_LEVEL_ACT_SDL:	/* SDL connection */
		spin_lock_bh(&ch->lock);
		ch->sdl.config.ifflags &= ~(SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING | SDL_IF_UP);
		ch->sdl.statem.rx_state = SDL_STATE_IDLE;
		ch->sdl.statem.tx_state = SDL_STATE_IDLE;
		spin_unlock_bh(&ch->lock);
		break;
	case XP_LEVEL_ACT_SDT:	/* SDT connection */
		spin_lock_bh(&ch->lock);
		ch->sdt.statem.daedr_state = SDT_STATE_IDLE;
		ch->sdl.statem.rx_state = SDL_STATE_IDLE;
		ch->sdt.statem.daedt_state = SDT_STATE_IDLE;
		ch->sdl.statem.tx_state = SDL_STATE_IDLE;
		ch->sdl.config.ifflags &= ~(SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING | SDL_IF_UP);
		spin_unlock_bh(&ch->lock);
		break;
	case XP_LEVEL_ACT_SL:	/* SL connection */
		spin_lock_bh(&ch->lock);
		sl_lsc_stop(ch);
		spin_unlock_bh(&ch->lock);
	}
	return (0);
      outstate:
	return (-EPROTO);
}

/** dl_reset_req: - process DL_RESET_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_RESET_REQ primitive
  */
static inline fastcall __unlikely int
dl_reset_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_reset_res: - process DL_RESET_RES primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_RESET_RES primitive
  */
static inline fastcall __unlikely int
dl_reset_res(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EPROTO);
}

/** dl_xid_req: - process DL_XID_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_XID_REQ primitive
  */
static inline fastcall __unlikely int
dl_xid_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_xid_res: - process DL_XID_RES primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_XID_RES primitive
  */
static inline fastcall __unlikely int
dl_xid_res(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EPROTO);
}

/** dl_test_req: - process DL_TEST_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_TEST_REQ primitive
  */
static inline fastcall __unlikely int
dl_test_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_test_res: - process DL_TEST_RES primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_TEST_RES primitive
  */
static inline fastcall __unlikely int
dl_test_res(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EPROTO);
}

/** dl_enabmulti_req: - process DL_ENABMULTI_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_ENABMULTI_REQ primitive
  */
static inline fastcall __unlikely int
dl_enabmulti_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_disabmulti_req: - process DL_DISABMULTI_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_DISABMULTI_REQ primitive
  */
static inline fastcall __unlikely int
dl_disabmulti_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-ENOENT);
}

/** dl_promsicon_req: - process DL_PROMISCON_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_PROMISCON_REQ primitive
  */
static inline fastcall __unlikely int
dl_promiscon_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_promiscoff_req: - process DL_PROMISCOFF_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_PROMISCOFF_REQ primitive
  */
static inline fastcall __unlikely int
dl_promiscoff_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_phys_addr_req: - process DL_PHYS_ADDR_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_PHYS_ADDR_REQ primitive
  *
  * TODO: There is no reason why we shouldn't deliver the signalling datalink/terminal identifier in
  * response to this message.
  */
static inline fastcall __unlikely int
dl_phys_addr_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_set_phys_addr_req: - process DL_SET_PHYS_ADDR_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_SET_PHYS_ADDR_REQ primitive
  *
  * TODO: There is no reason why we shouldn't set the signalling datalink/terminal identifier in
  * response to this message.
  */
static inline fastcall __unlikely int
dl_set_phys_addr_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_get_statistics_req: - process DL_GET_STATISTICS_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_GET_STATISTICS_REQ primitive
  *
  * TODO: There is no reason why we should not provide the current link statistics in response to
  * this primitive.
  */
static inline fastcall __unlikely int
dl_get_statistics_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

/** dl_monitor_link_layer: - process DL_MONITOR_LINK_LAYER primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_MONITOR_LINK_LAYER primitive
  *
  * This primitive is a Spider extension that we support with Linux Fast-STREAMS as well.  It
  * requests that link layer monitoring be performed.  I have no idea what the contents of the
  * primitive are, but I assume that it is empty other than the primitve type itself.  We want the
  * stream to be attached to an interface, but not yet bound to it.  I assume that the response to
  * the message is a DL_OK_ACK primtive.
  *
  * TODO: Implement this primitive.
  */
static inline fastcall __unlikely int
dl_monitor_link_layer(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}

#ifdef _SUN_SOURCE

#ifdef DL_NOTIFY_REQ
/** dl_notify_req: - process DL_NOTIFY_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_NOTIFY_REQ primitive
  *
  * There are several notifications that are pertinent to SS7 signalling links: DL_NOTE_LINK_DOWN,
  * DL_NOTE_LINK_UP, DL_NOTE_SDU_SIZE, DL_NOTE_PHYS_ADDR, DL_NOTE_PROMISC_ON_PHYS,
  * DL_NOTE_PROMISC_OFF_PHYS, DL_NOTE_CAPAB_RENEG.
  *
  * TODO: Implement this primitive.
  */
static inline fastcall __unlikely int
dl_notify_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_NOTIFY_REQ */

#ifdef DL_AGGR_REQ
/** dl_aggr_req: - process DL_AGGR_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_AGGR_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_aggr_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_AGGR_REQ */

#ifdef DL_UNAGGR_REQ
/** dl_unaggr_req: - process DL_UNAGGR_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_UNAGGR_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_unaggr_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_UNAGGR_REQ */

#ifdef DL_CAPABILITY_REQ
/** dl_capability_req: - process DL_CAPABILITY_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_CAPABILITY_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_capability_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_CAPABILITY_REQ */

#ifdef DL_CONTROL_REQ
/** dl_control_req: - process DL_CONTROL_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_CONTROL_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_control_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_CONTROL_REQ */

#ifdef DL_PASSIVE_REQ
/** dl_passive_req: - process DL_PASSIVE_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_PASSIVE_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_passive_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_PASSIVE_REQ */

#ifdef DL_INTR_MODE_REQ
/** dl_intr_mode_req: - process DL_INTR_MODE_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_INTR_MODE_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_intr_mode_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_INTR_MODE_REQ */

#endif				/* SUN_SOURCE */

#ifdef _HPUX_SOURCE

#ifdef DL_HP_PPA_REQ
/** dl_hp_ppa_req: - process DL_HP_PPA_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_HP_PPA_REQ primitive
  *
  * See DL_HP_PPA_REQ(7).  The DL_HP_PPA_REQ primitive is used to obtain a list of all of the valid
  * Physical Points of Attachment (PPA) currently installed in the system and known to the DLS
  * provider.  The primitive requests that the DLS provider assemble a list of all PPA known to the
  * provider and acknowledge the successful receipt of the primitive by issuing a DL_HP_PPA_ACK(7)
  * primitive containing the list of PPA and PPA associated information.
  *
  * TODO: Implement this primitive.
  */
static inline fastcall __unlikely int
dl_hp_ppa_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_HP_PPA_REQ */

#ifdef DL_HP_MULTICAST_LIST_REQ
/** dl_hp_multicast_list_req: - process DL_HP_MULTICAST_LIST_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_HP_MULTICAST_LIST_REQ primitive
  *
  * This primitive is unsupported as it has no equivalent for SS7.
  */
static inline fastcall __unlikely int
dl_hp_multicast_list_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_HP_MULTICAST_LIST_REQ */

#ifdef DL_HP_RAWDATA_REQ
/** dl_hp_rawdata_req: - process DL_HP_RAWDATA_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_HP_RAWDATA_REQ primitive
  *
  * TODO: Implement this primitive.
  */
static inline fastcall __unlikely int
dl_hp_rawdata_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_HP_RAWDATA_REQ */

#ifdef DL_HP_HW_RESET_REQ
/** dl_hp_hw_reset_req: - process DL_HP_HW_RESET_REQ primtive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the DL_HP_HW_RESET_REQ primitive
  *
  * TODO: Implement this primitive.
  */
static inline fastcall __unlikely int
dl_hp_hw_reset_req(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return (-EOPNOTSUPP);
}
#endif				/* DL_HP_HW_RESET_REQ */

#endif				/* _HPUX_SOURCE */

/* 
 *  =========================================================================
 *
 *  IO Controls
 *
 *  =========================================================================
 */
STATIC __unlikely int
dsx_info_size(dsx_info_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->info->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->info->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->info->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->info->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocginfo_dflt(dsx_info_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocginfo_span(struct sp *sp, dsx_info_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocginfo_chan(struct sp *sp, struct ch *ch, dsx_info_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocginfo_frac(struct sp *sp, struct ch *ch, dsx_info_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocginfo(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	dsx_info_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = dsx_info_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	switch (arg->type) {
		struct sp *sp;
		struct ch *ch;

	case DSX_OBJ_TYPE_DFLT:
		if (arg->id != 0)
			return (-ESRCH);
		if ((ret = dsx_iocginfo_dflt(hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_SPAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if ((ret = dsx_iocginfo_span(sp, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_CHAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocginfo_chan(sp, ch, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_FRAC:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocginfo_frac(sp, ch, hdr)) < 0)
			return (ret);
		break;
	default:
		return (-EINVAL);
	}
	mi_copyout(q, mp);
	return (0);
}

STATIC __unlikely int
dsx_option_size(dsx_option_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->option->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->option->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->option->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->option->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgoption_dflt(dsx_option_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocgoption_span(struct sp *sp, dsx_option_t * arg)
{
	struct dsx_opt_conf_span *val = (typeof(val)) (arg + 1);

	val->dsx1SendCode = DSX1SENDCODE_DSX1SENDNOCODE;
	val->dsx1LoopbackConfig = DSX1LOOPBACKCONFIG_DSX1NOLOOP;
	switch (sp->config.ifclock) {
	case SDL_CLOCK_INT:
	case SDL_CLOCK_EXT:
	case SDL_CLOCK_MASTER:
		val->dsx1TransmitClockSource = DSX1TRANSMITCLOCKSOURCE_LOCALTIMING;
		break;
	case SDL_CLOCK_LOOP:
		val->dsx1TransmitClockSource = DSX1TRANSMITCLOCKSOURCE_LOOPTIMING;
		break;
	case SDL_CLOCK_SLAVE:
		val->dsx1TransmitClockSource = DSX1TRANSMITCLOCKSOURCE_THROUGHTIMING;
		break;
	default:
	case SDL_CLOCK_DPLL:
	case SDL_CLOCK_ABR:
	case SDL_CLOCK_SHAPER:
	case SDL_CLOCK_TICK:
	case SDL_CLOCK_NONE:
		val->dsx1TransmitClockSource = DSX1TRANSMITCLOCKSOURCE_ADAPTIVE;
		break;
	}
	val->dsx1Fdl = 0;
	val->dsx1Fdl |= (1 << DSX1FDL_OTHER);
	val->dsx1Fdl |= (1 << DSX1FDL_DSX1ANSIT1403);
	val->dsx1Fdl |= (1 << DSX1FDL_DSX1ATT54016);
	val->dsx1Fdl |= (1 << DSX1FDL_DSX1FDLNONE);
	val->dsx1LineStatusChangeTrapEnable = DSX1LINESTATUSCHANGETRAPENABLE_DISABLED;
	if (sp->config.iftxlevel & ~0x7) {
		val->dsx1LineMode = DSX1LINEMODE_DSU;
		val->dsx1LineLength = 0;
		val->dsx1LineBuildOut = DSX1LINEBUILDOUT_NOTAPPLICABLE;
		val->dsx1LineImpedance = DSX1LINEIMPEDANCE_NOTAPPLICABLE;
	} else if (0 < sp->config.iftxlevel && sp->config.iftxlevel < 5) {
		val->dsx1LineMode = DSX1LINEMODE_DSU;
		val->dsx1LineBuildOut = DSX1LINEBUILDOUT_NOTAPPLICABLE;
		if (sp->config.ifgtype != SDL_GTYPE_E1) {
			val->dsx1LineImpedance = DSX1LINEIMPEDANCE_BALANCED100OHMS;
			switch (sp->config.iftxlevel) {
			case 0:
				val->dsx1LineLength = 40;	/* 40 meters: 133ft */
				break;
			case 1:
				val->dsx1LineLength = 80;	/* 80 meters: 266ft */
				break;
			case 2:
				val->dsx1LineLength = 120;	/* 120 meters: 399ft */
				break;
			case 3:
				val->dsx1LineLength = 160;	/* 160 meters: 533ft */
				break;
			case 4:
				val->dsx1LineLength = 200;	/* 200 meters: 666ft */
				break;
			}
		} else {
			switch (sp->config.iftxlevel) {
			case 1:
			case 3:
				val->dsx1LineImpedance = DSX1LINEIMPEDANCE_UNBALANCED75OHMS;
				break;
			case 2:
			case 4:
				val->dsx1LineImpedance = DSX1LINEIMPEDANCE_BALANCED120OHMS;
				break;
			}
		}
	} else {
		val->dsx1LineLength = 0;
		val->dsx1LineMode = DSX1LINEMODE_CSU;
		if (sp->config.ifgtype != SDL_GTYPE_E1) {
			val->dsx1LineImpedance = DSX1LINEIMPEDANCE_BALANCED100OHMS;
			switch (sp->config.iftxlevel) {
			case 0:
				val->dsx1LineBuildOut = DSX1LINEBUILDOUT_ZERODB;
				break;
			case 5:
				val->dsx1LineBuildOut = DSX1LINEBUILDOUT_NEG75DB;
				break;
			case 6:
				val->dsx1LineBuildOut = DSX1LINEBUILDOUT_NEG15DB;
				break;
			case 7:
				val->dsx1LineBuildOut = DSX1LINEBUILDOUT_NEG225DB;
				break;
			}
		} else {
			switch (sp->config.iftxlevel) {
			case 0:
			case 7:
				val->dsx1LineImpedance = DSX1LINEIMPEDANCE_NOTAPPLICABLE;
				break;
			case 5:
				val->dsx1LineImpedance = DSX1LINEIMPEDANCE_UNBALANCED75OHMS;
				break;
			case 6:
				val->dsx1LineImpedance = DSX1LINEIMPEDANCE_BALANCED120OHMS;
				break;
			}
		}
	}
	return (0);
}

noinline __unlikely int
dsx_iocgoption_chan(struct sp *sp, struct ch *ch, dsx_option_t * arg)
{
	struct dsx_opt_conf_chan *val = (typeof(val)) (arg + 1);

	val->dsx0TransmitCodesEnable = DSX0TRANSMITCODESENABLE_FALSE;
	return (0);
}

noinline __unlikely int
dsx_iocgoption_frac(struct sp *sp, struct ch *ch, dsx_option_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocgoption(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	dsx_option_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = dsx_option_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	switch (arg->id) {
		struct sp *sp;
		struct ch *ch;

	case DSX_OBJ_TYPE_DFLT:
		if (arg->id != 0)
			return (-ESRCH);
		if ((ret = dsx_iocgoption_dflt(hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_SPAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if ((ret = dsx_iocgoption_span(sp, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_CHAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocgoption_chan(sp, ch, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_FRAC:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocgoption_frac(sp, ch, hdr)) < 0)
			return (ret);
		break;
	default:
		return (-EINVAL);
	}
	mi_copyout(q, mp);
	return (0);
}

noinline __unlikely int
dsx_test_options_dflt(dsx_option_t * arg)
{
	return (0);

}

noinline __unlikely int
dsx_test_options_span(struct sp *sp, dsx_option_t * arg)
{
	struct dsx_opt_conf_span *val = (typeof(val)) (arg + 1);

	switch (val->dsx1SendCode) {
	case DSX1SENDCODE_DSX1SENDNOCODE:
		break;
	case DSX1SENDCODE_DSX1SENDLINECODE:
	case DSX1SENDCODE_DSX1SENDPAYLOADCODE:
	case DSX1SENDCODE_DSX1SENDRESETCODE:
	case DSX1SENDCODE_DSX1SENDQRS:
	case DSX1SENDCODE_DSX1SEND511PATTERN:
	case DSX1SENDCODE_DSX1SEND3IN24PATTERN:
	case DSX1SENDCODE_DSX1SENDOTHERTESTPATTERN:
		goto eopnotsupp;
	default:
		goto einval;
	}
	switch (val->dsx1LoopbackConfig) {
	case DSX1LOOPBACKCONFIG_DSX1NOLOOP:
		break;
	case DSX1LOOPBACKCONFIG_DSX1PAYLOADLOOP:
	case DSX1LOOPBACKCONFIG_DSX1LINELOOP:
	case DSX1LOOPBACKCONFIG_DSX1OTHERLOOP:
	case DSX1LOOPBACKCONFIG_DSX1INWARDLOOP:
	case DSX1LOOPBACKCONFIG_DSX1DUALLOOP:
		goto eopnotsupp;
	default:
		goto einval;
	}
	switch (val->dsx1TransmitClockSource) {
	case DSX1TRANSMITCLOCKSOURCE_LOOPTIMING:
	case DSX1TRANSMITCLOCKSOURCE_LOCALTIMING:
	case DSX1TRANSMITCLOCKSOURCE_THROUGHTIMING:
		break;
	case DSX1TRANSMITCLOCKSOURCE_ADAPTIVE:
		goto eopnotsupp;
	default:
		goto einval;
	}
	if (val->dsx1Fdl & (1 << DSX1FDL_OTHER))
		goto eopnotsupp;
	if (val->dsx1Fdl & (1 << DSX1FDL_DSX1ANSIT1403))
		goto eopnotsupp;
	if (val->dsx1Fdl & (1 << DSX1FDL_DSX1ATT54016))
		goto eopnotsupp;
	if (val->dsx1Fdl & (1 << DSX1FDL_DSX1FDLNONE))
		goto eopnotsupp;
	if (sp->config.ifgtype != SDL_GTYPE_E1) {
		if (0 > val->dsx1LineLength || val->dsx1LineLength > 200)
			goto einval;
	}
	switch (val->dsx1LineStatusChangeTrapEnable) {
	case DSX1LINESTATUSCHANGETRAPENABLE_ENABLED:
	case DSX1LINESTATUSCHANGETRAPENABLE_DISABLED:
		break;
	default:
		goto einval;
	}
	switch (val->dsx1LineMode) {
	case DSX1LINEMODE_CSU:
	case DSX1LINEMODE_DSU:
		break;
	default:
		goto einval;
	}
	switch (val->dsx1LineBuildOut) {
	case DSX1LINEBUILDOUT_NOTAPPLICABLE:
		if (val->dsx1LineMode == DSX1LINEMODE_CSU && sp->config.ifgtype != SDL_GTYPE_E1)
			goto einval;
		break;
	case DSX1LINEBUILDOUT_NEG75DB:
	case DSX1LINEBUILDOUT_NEG15DB:
	case DSX1LINEBUILDOUT_NEG225DB:
	case DSX1LINEBUILDOUT_ZERODB:
		if (val->dsx1LineMode != DSX1LINEMODE_CSU || sp->config.ifgtype == SDL_GTYPE_E1)
			goto einval;
		break;
	default:
		goto einval;
	}
	switch (val->dsx1LineImpedance) {
	case DSX1LINEIMPEDANCE_NOTAPPLICABLE:
		if (sp->config.iftxlevel <= 8)
			goto einval;
		break;
	case DSX1LINEIMPEDANCE_BALANCED100OHMS:
		if (sp->config.ifgtype == SDL_GTYPE_E1)
			goto einval;
		break;
	case DSX1LINEIMPEDANCE_UNBALANCED75OHMS:
	case DSX1LINEIMPEDANCE_BALANCED120OHMS:
		if (sp->config.ifgtype != SDL_GTYPE_E1)
			goto einval;
		break;
	default:
		goto einval;
	}
	return (0);
      eopnotsupp:
	return (-EOPNOTSUPP);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
dsx_test_options_chan(struct sp *sp, struct ch *ch, dsx_option_t * arg)
{
	struct dsx_opt_conf_chan *val = (typeof(val)) (arg + 1);

	switch (val->dsx0TransmitCodesEnable) {
	case DSX0TRANSMITCODESENABLE_FALSE:
		break;
	case DSX0TRANSMITCODESENABLE_TRUE:
		goto eopnotsupp;
	default:
		goto einval;
	}
	return (0);
      eopnotsupp:
	return (-EOPNOTSUPP);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
dsx_test_options_frac(struct sp *sp, struct ch *ch, dsx_option_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_test_options(queue_t *q, mblk_t *mp, dsx_option_t * arg)
{
	int ret;

	switch (arg->type) {
		struct sp *sp;
		struct ch *ch;

	case DSX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			return (-ESRCH);
		if ((ret = dsx_test_options_dflt(arg)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if ((ret = dsx_test_options_span(sp, arg)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_test_options_chan(sp, ch, arg)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_FRAC:	/* fractional */
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_test_options_frac(sp, ch, arg)) < 0)
			return (ret);
		break;
	default:
		return (-EINVAL);
	}
	return (0);
}

noinline __unlikely int
dsx_set_options_dflt(dsx_option_t * arg)
{
	if (arg->id != 0)
		goto esrch;
	return (0);
      esrch:
	return (-ESRCH);
}

noinline __unlikely int
dsx_set_options_span(dsx_option_t * arg)
{
	struct sp *sp;
	struct dsx_opt_conf_span *val = (typeof(val)) (arg + 1);
	uint txlevel = 0, span_reconfig = 0;

	if (!(sp = sp_find(arg->id)))
		goto esrch;
	switch (val->dsx1LoopbackConfig) {
	case DSX1LOOPBACKCONFIG_DSX1NOLOOP:
		if (sp->config.ifgmode & SDL_GMODE_LOC_LB) {
			span_reconfig = 1;
			sp->config.ifgmode &= ~SDL_GMODE_LOC_LB;
		}
		if (sp->config.ifgmode & SDL_GMODE_REM_LB) {
			span_reconfig = 1;
			sp->config.ifgmode &= ~SDL_GMODE_REM_LB;
		}
		break;
	}
	switch (val->dsx1TransmitClockSource) {
	case DSX1TRANSMITCLOCKSOURCE_LOOPTIMING:
		if (sp->config.ifclock != SDL_CLOCK_LOOP) {
			span_reconfig = 1;
			sp->config.ifclock = SDL_CLOCK_LOOP;
		}
		break;
	case DSX1TRANSMITCLOCKSOURCE_LOCALTIMING:
		if (sp->config.ifclock != SDL_CLOCK_EXT) {
			span_reconfig = 1;
			sp->config.ifclock = SDL_CLOCK_EXT;
		}
		break;
	case DSX1TRANSMITCLOCKSOURCE_THROUGHTIMING:
		if (sp->config.ifclock != SDL_CLOCK_INT) {
			span_reconfig = 1;
			sp->config.ifclock = SDL_CLOCK_INT;
		}
		break;
	}
	if (sp->config.ifgtype == SDL_GTYPE_E1) {
		switch (val->dsx1LineImpedance) {
		case DSX1LINEIMPEDANCE_UNBALANCED75OHMS:
			txlevel = 1;
			break;
		case DSX1LINEIMPEDANCE_BALANCED120OHMS:
			txlevel = 2;
			break;
		}
	} else {
		val->dsx1LineImpedance = DSX1LINEIMPEDANCE_BALANCED100OHMS;
		switch (val->dsx1LineMode) {
		case DSX1LINEMODE_DSU:
			if (0 <= val->dsx1LineLength && val->dsx1LineLength < 40) {
				txlevel = 0;
			} else if (40 <= val->dsx1LineLength && val->dsx1LineLength < 80) {
				txlevel = 1;
			} else if (80 <= val->dsx1LineLength && val->dsx1LineLength < 120) {
				txlevel = 2;
			} else if (120 <= val->dsx1LineLength && val->dsx1LineLength < 160) {
				txlevel = 3;
			} else if (160 <= val->dsx1LineLength && val->dsx1LineLength <= 200) {
				txlevel = 4;
			}
			break;
		case DSX1LINEMODE_CSU:
			switch (val->dsx1LineBuildOut) {
			case DSX1LINEBUILDOUT_NOTAPPLICABLE:
			case DSX1LINEBUILDOUT_ZERODB:
				txlevel = 0;
				break;
			case DSX1LINEBUILDOUT_NEG75DB:
				txlevel = 5;
				break;
			case DSX1LINEBUILDOUT_NEG15DB:
				txlevel = 6;
				break;
			case DSX1LINEBUILDOUT_NEG225DB:
				txlevel = 7;
				break;
			}
			break;
		}
	}
	if (sp->config.iftxlevel != txlevel) {
		span_reconfig = 1;
		sp->config.iftxlevel = txlevel;
	}
	if (span_reconfig && (sp->config.ifflags & SDL_IF_UP))
		xp_span_reconfig(sp);
	return (0);
      esrch:
	return (-ESRCH);
}

noinline __unlikely int
dsx_set_options_chan(dsx_option_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_set_options_frac(dsx_option_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_set_options(queue_t *q, mblk_t *mp, dsx_option_t * arg)
{
	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		return dsx_set_options_dflt(arg);
	case DSX_OBJ_TYPE_SPAN:
		return dsx_set_options_span(arg);
	case DSX_OBJ_TYPE_CHAN:
		return dsx_set_options_chan(arg);
	case DSX_OBJ_TYPE_FRAC:
		return dsx_set_options_frac(arg);
	}
	return (-EINVAL);
}

noinline __unlikely int
dsx_iocsoption(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	dsx_option_t *arg = (typeof(arg)) dp->b_rptr;
	int ret;

	if ((ret = dsx_test_options(q, mp, arg)))
		return (ret);
	return dsx_set_options(q, mp, arg);
}

STATIC __unlikely int
dsx_lconfig_size(dsx_config_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		uint card, span;

	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(uint);
		break;
	case DSX_OBJ_TYPE_SPAN:
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++)
				size += sizeof(uint);
		}
		break;
	case DSX_OBJ_TYPE_CHAN:
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++)
				if ((sp = cd->spans[span])) {
					switch (sp->config.ifgtype) {
					case SDL_GTYPE_E1:
						size += sizeof(uint) * 31;
						break;
					case SDL_GTYPE_J1:
					case SDL_GTYPE_T1:
						size += sizeof(uint) * 24;
						break;
					}
				}
		}
		break;
	case DSX_OBJ_TYPE_FRAC:
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

/**
 * dsx_ioclconfig: - list configuration input-output control
 * @q: active queue (management stream write queue)
 * @mp: the input-output control
 *
 * Lists as a number of unsigned integers the identifiers of all of the elements of a type that will
 * fit into the buffer area.  When successful, returns the number of elements (whether they would
 * fit into the buffer or not).
 */
noinline __unlikely int
dsx_ioclconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	dsx_config_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	uint num = 0, *val = (typeof(val)) (arg + 1);

	if ((ret = dsx_lconfig_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		uint card, span, chan;

	case DSX_OBJ_TYPE_DFLT:
		if ((unsigned char *) (val + 1) <= mp->b_cont->b_wptr)
			*val = 0;
		val++;
		num++;
		break;
	case DSX_OBJ_TYPE_SPAN:
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++, val++, num++)
				if ((unsigned char *) (val + 1) <= mp->b_cont->b_wptr)
					*val = (DRV_ID << 16) | (cd->card << 12) | (span << 8);
		}
		break;
	case DSX_OBJ_TYPE_CHAN:
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++) {
				if (!(sp = cd->spans[span]))
					continue;
				switch (sp->config.ifgtype) {
				case SDL_GTYPE_E1:
					for (chan = 1; chan <= 31; chan++, val++, num++)
						if ((unsigned char *) (val + 1) <= mp->b_cont->b_wptr)
							*val = (DRV_ID << 16) | (cd-> card << 12) | (span << 8) | (chan << 0);
					break;
				case SDL_GTYPE_T1:
				case SDL_GTYPE_J1:
					for (chan = 1; chan <= 24; chan++, val++, num++)
						if ((unsigned char *) (val + 1) <= mp->b_cont->b_wptr)
							*val = (DRV_ID << 16) | (cd-> card << 12) | (span << 8) | (chan << 0);
					break;
				}
			}
		}
		break;
	case DSX_OBJ_TYPE_FRAC:
		break;
	default:
		return (-EINVAL);
	}
	mp->b_cont->b_wptr = (unsigned char *) val;
	return (num);
}

STATIC __unlikely int
dsx_config_size(dsx_config_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->config->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->config->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->config->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->config->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgconfig_dflt(dsx_config_t * arg)
{
	return (0);
}

noinline __unlikely int
dsx_iocgconfig_span(struct sp *sp, dsx_config_t * arg)
{
	struct dsx_conf_span *val = (typeof(val)) (arg + 1);

	val->dsx1LineIndex = (DRV_ID << 16) | (sp->cd->card << 12);
	val->dsx1IfIndex = (DRV_ID << 16) | (sp->cd->card << 12) | (sp->span << 8);
	val->dsx1Ds1ChannelNumber = 0;
	val->dsx1CircuitIdentifier[0] = '\0';
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E2:
		val->dsx1LineType = DSX1LINETYPE_DSX1E2;
		val->dsx1LineCoding = DSX1LINECODING_OTHER;
		break;
	case SDL_GTYPE_T2:
	case SDL_GTYPE_T3:
		val->dsx1LineType = DSX1LINETYPE_DSX1DS2M12;
		val->dsx1LineCoding = DSX1LINECODING_DSX1B6ZS;
		break;
	case SDL_GTYPE_E1:
		switch (sp->config.ifframing) {
		case SDL_FRAMING_CAS:
			switch (sp->config.ifgcrc) {
			case SDL_GCRC_CRC4:
				val->dsx1LineType = DSX1LINETYPE_DSX1E1CRCMF;
				break;
			case SDL_GCRC_CRC5:
				val->dsx1LineType = DSX1LINETYPE_DSX1E1MF;
				break;
			}
			break;
		case SDL_FRAMING_CCS:
			switch (sp->config.ifgcrc) {
			case SDL_GCRC_CRC4:
				val->dsx1LineType = DSX1LINETYPE_DSX1E1CRC;
				break;
			case SDL_GCRC_CRC5:
				val->dsx1LineType = DSX1LINETYPE_DSX1E1;
				break;
			}
			break;
		}
		switch (sp->config.ifcoding) {
		case SDL_CODING_HDB3:
			val->dsx1LineCoding = DSX1LINECODING_DSX1HDB3;
			break;
		case SDL_CODING_AMI:
			val->dsx1LineCoding = DSX1LINECODING_DSX1AMI;
			break;
		}
		break;
	case SDL_GTYPE_T1:
		switch (sp->config.ifframing) {
		case SDL_FRAMING_D4:
			val->dsx1LineType = DSX1LINETYPE_DSX1D4;
			break;
		case SDL_FRAMING_ESF:
			val->dsx1LineType = DSX1LINETYPE_DSX1ESF;
			break;
		}
		switch (sp->config.ifcoding) {
		case SDL_CODING_AMI:
			val->dsx1LineCoding = DSX1LINECODING_DSX1AMI;
			val->dsx1SignalMode = DSX1SIGNALMODE_ROBBEDBIT;
			break;
		case SDL_CODING_B8ZS:
			val->dsx1LineCoding = DSX1LINECODING_DSX1B8ZS;
			val->dsx1SignalMode = DSX1SIGNALMODE_NONE;
			break;
		case SDL_CODING_B6ZS:
			val->dsx1LineCoding = DSX1LINECODING_DSX1B6ZS;
			val->dsx1SignalMode = DSX1SIGNALMODE_NONE;
			break;
		}
		break;
	case SDL_GTYPE_J1:
		val->dsx1LineType = DSX1LINETYPE_DSX1J1ESF;
		switch (sp->config.ifcoding) {
		case SDL_CODING_AMI:
			val->dsx1LineCoding = DSX1LINECODING_DSX1AMI;
			break;
		case SDL_CODING_B8ZS:
			val->dsx1LineCoding = DSX1LINECODING_DSX1B8ZS;
			break;
		case SDL_CODING_B6ZS:
			val->dsx1LineCoding = DSX1LINECODING_DSX1B6ZS;
			break;
		}
		break;
	}
	val->dsx1Channelization = DSX1CHANNELIZATION_ENABLEDDS0;
	return (0);
}

noinline __unlikely int
dsx_iocgconfig_chan(struct sp *sp, struct ch *ch, dsx_config_t * arg)
{
	struct dsx_conf_chan *val = (typeof(val)) (arg + 1);
	uint iftype;

	val->ifIndex = arg->id;
	val->dsx1IfIndex = (DRV_ID << 16) | (sp->cd->card << 12) | (sp->span << 8);
	val->dsx0Ds0ChannelNumber = arg->id & 0x1f;
	val->dsx0CircuitIdentifier[0] = '\0';
	iftype = ch ? ch->sdl.config.iftype : sp->config.iftype;
	switch (iftype) {
	case SDL_TYPE_DS0:
		val->dsx0RobbedBitSignalling = DSX0ROBBEDBITSIGNALLING_FALSE;
		break;
	case SDL_TYPE_DS0A:
		val->dsx0RobbedBitSignalling = DSX0ROBBEDBITSIGNALLING_TRUE;
		break;
	}
	val->dsx0IdleCode = 0x00;
	val->dsx0SeizedCode = 0x0f;
	return (0);
}

noinline __unlikely int
dsx_iocgconfig_frac(struct sp *sp, struct ch *ch, dsx_config_t * arg)
{
	return (-ESRCH);
}

/**
 * dsx_iocgconfig: - get configuration information for an object
 * @q: active queue (management stream write queue)
 * @mp: the input-output control
 * @arg; the configuration structure
 */
noinline __unlikely int
dsx_iocgconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	dsx_config_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	arg->cmd = DSX_GET;

	if ((ret = dsx_config_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	switch (arg->type) {
		struct sp *sp;
		struct ch *ch;

	case DSX_OBJ_TYPE_DFLT:
		if (arg->id != 0)
			return (-ESRCH);
		if ((ret = dsx_iocgconfig_dflt(hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_SPAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if ((ret = dsx_iocgconfig_span(sp, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_CHAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocgconfig_chan(sp, ch, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_FRAC:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocgconfig_frac(sp, ch, hdr)) < 0)
			return (ret);
		break;
	default:
		return (-EINVAL);
	}
	mi_copyout(q, mp);
	return (0);
}

noinline __unlikely int
dsx_iocsconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_ioctconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_ioccconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_statem_size(dsx_statem_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->statem->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->statem->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->statem->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->statem->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgstatem(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_ioccmreset(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_status_size(dsx_status_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->status->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->status->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->status->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->status->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgstatus(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_iocsstatus(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_ioccstatus(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_stats_size(dsx_stats_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->stats->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->stats->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->stats->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->stats->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgstatsp(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_iocsstatsp(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_iocgstats_dflt(dsx_stats_t * arg)
{
	struct dsx_stats_dflt *val = (typeof(val)) (arg + 1);

	/* there are no default dsx stats */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
dsx_iocgstats_span(struct sp *sp, dsx_stats_t * arg)
{
	struct dsx_stats_span *val = (typeof(val)) (arg + 1);

	/* FIXME: get these */
	bzero(val, sizeof(*val));
	switch (arg->subtype) {
	case DSX_SUBTYPE_NEAREND:
	{
		uint interval, valid, invalid;
		struct st *st, total;

		for (valid = 0, invalid = 0, interval = 0; interval < 96; interval++) {
			st = &sp->hist[interval];

			if (st->ValidData)
				valid++;
			else
				invalid++;
		}
		val->NearEnd.dsx1ValidIntervals = valid;
		val->NearEnd.dsx1InvalidIntervals = invalid;

		if (arg->interval == 0)
			st = &sp->stats[2];
		else if (arg->interval > 96 && arg->interval != 0xffffffff)
			return (-EINVAL);
		else if (sp->hist == NULL)
			return (-EINVAL);
		else if (arg->interval == 0xffffffff) {
			bzero(&total, sizeof(total));
			for (interval = 0; interval < 96; interval++) {
				st = &sp->hist[interval];

				if (st->ValidData) {
					total.ESs += st->ESs;
					total.SESs += st->SESs;
					total.SEFSs += st->SEFSs;
					total.UASs += st->UASs;
					total.CSSs += st->CSSs;
					total.PCVs += st->PCVs;
					total.LESs += st->LESs;
					total.BESs += st->BESs;
					total.DMs += st->DMs;
					total.LCVs += st->LCVs;
				}
			}
			if (valid)
				total.ValidData = DSX1FARENDINTERVALVALIDDATA_TRUE;
			else
				total.ValidData = DSX1FARENDINTERVALVALIDDATA_FALSE;
			st = &total;
		} else
			st = &sp->hist[(sp->curr + (arg->interval - 1)) % 96];
		val->NearEnd.dsx1ESs = st->ESs;
		val->NearEnd.dsx1SESs = st->SESs;
		val->NearEnd.dsx1SEFSs = st->SEFSs;
		val->NearEnd.dsx1UASs = st->UASs;
		val->NearEnd.dsx1CSSs = st->CSSs;
		val->NearEnd.dsx1PCVs = st->PCVs;
		val->NearEnd.dsx1LESs = st->LESs;
		val->NearEnd.dsx1BESs = st->BESs;
		val->NearEnd.dsx1DMs = st->DMs;
		val->NearEnd.dsx1LCVs = st->LCVs;
		val->NearEnd.dsx1ValidData = st->ValidData;
		break;
	}
	case DSX_SUBTYPE_FAREND:
	{
		val->FarEnd.dsx1ValidIntervals = 0;
		val->FarEnd.dsx1InvalidIntervals = 0;
		val->FarEnd.dsx1ESs = 0;
		val->FarEnd.dsx1SESs = 0;
		val->FarEnd.dsx1SEFSs = 0;
		val->FarEnd.dsx1UASs = 0;
		val->FarEnd.dsx1CSSs = 0;
		val->FarEnd.dsx1PCVs = 0;
		val->FarEnd.dsx1LESs = 0;
		val->FarEnd.dsx1BESs = 0;
		val->FarEnd.dsx1DMs = 0;
		val->FarEnd.dsx1ValidData = DSX1FARENDINTERVALVALIDDATA_FALSE;
		break;
	}
	default:
		return (-EINVAL);
	}
	return (0);
}

noinline __unlikely int
dsx_iocgstats_chan(struct sp *sp, struct ch *ch, dsx_stats_t * arg)
{
	struct dsx_stats_chan *val = (typeof(val)) (arg + 1);

	/* there are no channel dsx stats */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
dsx_iocgstats_frac(struct sp *sp, struct ch *ch, dsx_stats_t * arg)
{
	struct dsx_stats_frac *val = (typeof(val)) (arg + 1);

	/* there are no fractional dsx stats */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
dsx_iocgstats(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	dsx_stats_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = dsx_stats_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	switch (arg->type) {
		struct sp *sp;
		struct ch *ch;

	case DSX_OBJ_TYPE_DFLT:
		if (arg->id != 0)
			return (-ESRCH);
		if ((ret = dsx_iocgstats_dflt(hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_SPAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if ((ret = dsx_iocgstats_span(sp, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_CHAN:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocgstats_chan(sp, ch, hdr)) < 0)
			return (ret);
		break;
	case DSX_OBJ_TYPE_FRAC:
		if (!(sp = sp_find(arg->id)))
			return (-ESRCH);
		if (IS_ERR((ch = ch_find(arg->id))))
			return (-ESRCH);
		if ((ret = dsx_iocgstats_frac(sp, ch, hdr)) < 0)
			return (ret);
		break;
	default:
		return (-EINVAL);
	}
	mi_copyout(q, mp);
	return (0);
}

noinline __unlikely int
dsx_ioccstats(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_notify_size(dsx_notify_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->events->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->events->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->events->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->events->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgnotify(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_iocsnotify(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
dsx_ioccnotify(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_attr_size(int type)
{
	dsx_attr_t *arg;
	int size = sizeof(*arg);

	switch (type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->attrs->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->attrs->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->attrs->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->attrs->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_iocgattr(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	(void) &dsx_attr_size;
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_mgmt_size(dsx_mgmt_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case DSX_OBJ_TYPE_DFLT:
		size += sizeof(arg->action->dflt);
		break;
	case DSX_OBJ_TYPE_SPAN:
		size += sizeof(arg->action->span);
		break;
	case DSX_OBJ_TYPE_CHAN:
		size += sizeof(arg->action->chan);
		break;
	case DSX_OBJ_TYPE_FRAC:
		size += sizeof(arg->action->frac);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
dsx_ioccmgmt(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

STATIC __unlikely int
dsx_pass_size(dsx_pass_t * arg)
{
	int size = sizeof(*arg);

	size += arg->ctl_length;
	size += arg->dat_length;
	return (size);
}

noinline __unlikely int
dsx_ioccpass(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

/*
 *  =========================================================================
 *
 *  MX INPUT-OUTPUT CONTROL COMMAND ACTIONS
 *
 *  =========================================================================
 */

STATIC __unlikely int
mx_info_size(mx_info_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->info->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->info->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->info->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->info->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->info->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->info->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->info->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->info->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocginfo_dflt(mx_info_t * arg)
{
	struct mx_info_dflt *val = (typeof(val)) (arg + 1);

	val->mxDrivIdnum = DRV_ID;
	val->mxDrivMajor = CMAJOR_0;
	strncpy(val->mxDrivDescription, SL_X400P_DESCRIP, sizeof(val->mxDrivDescription));
	strncpy(val->mxDrivRevision, SL_X400P_REVISION, sizeof(val->mxDrivRevision));
	strncpy(val->mxDrivCopyright, SL_X400P_COPYRIGHT, sizeof(val->mxDrivCopyright));
	strncpy(val->mxDrivSupportedDevice, SL_X400P_DEVICE, sizeof(val->mxDrivSupportedDevice));
	strncpy(val->mxDrivContact, SL_X400P_CONTACT, sizeof(val->mxDrivContact));
	val->mxDrivLicense = MXDRIVLICENSE_GPL;
	memcpy(val->mxDrivDate, "\x07\xd9\x01\x0e\x0c\x21\x08\x00\x00\x00\x00\x00", 12);
	return (0);
}

noinline __unlikely int
mx_iocginfo_sync(struct sg *sg, mx_info_t * arg)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
mx_iocginfo_card(struct cd *cd, mx_info_t * arg)
{
	struct mx_info_card *val;

	val = (typeof(val)) (arg + 1);
	/* FIXME: complete this */
	switch (cd->board) {
	case PLX9030:
		val->mxCardType = X400PCARDTYPE_PLX9030;
		break;
	case PLXDEVBRD:
		val->mxCardType = X400PCARDTYPE_PLXDEVBRD;
		break;
	case X400P:
		val->mxCardType = X400PCARDTYPE_X400P;
		break;
	case E400P:
		val->mxCardType = X400PCARDTYPE_E400P;
		break;
	case T400P:
		val->mxCardType = X400PCARDTYPE_T400P;
		break;
	case X400PSS7:
		val->mxCardType = X400PCARDTYPE_X400PSS7;
		break;
	case E400PSS7:
		val->mxCardType = X400PCARDTYPE_E400PSS7;
		break;
	case T400PSS7:
		val->mxCardType = X400PCARDTYPE_T400PSS7;
		break;
	case V400P:
		val->mxCardType = X400PCARDTYPE_V400P;
		break;
	case V400PE:
		val->mxCardType = X400PCARDTYPE_V400PE;
		break;
	case V400PT:
		val->mxCardType = X400PCARDTYPE_V400PT;
		break;
	case V401PE:
		val->mxCardType = X400PCARDTYPE_V401PE;
		break;
	case V401PT:
		val->mxCardType = X400PCARDTYPE_V401PT;
		break;
	case A400P:
		val->mxCardType = X400PCARDTYPE_A400P;
		break;
	case AE400P:
		val->mxCardType = X400PCARDTYPE_AE400P;
		break;
	case AT400P:
		val->mxCardType = X400PCARDTYPE_AT400P;
		break;
	case A400PE:
		val->mxCardType = X400PCARDTYPE_A400PE;
		break;
	case A400PT:
		val->mxCardType = X400PCARDTYPE_A400PT;
		break;
	case CP100:
		val->mxCardType = X400PCARDTYPE_CP100;
		break;
	case CP100P:
		val->mxCardType = X400PCARDTYPE_CP100P;
		break;
	case CP100E:
		val->mxCardType = X400PCARDTYPE_CP100E;
		break;
	case CP200:
		val->mxCardType = X400PCARDTYPE_CP200;
		break;
	case CP200P:
		val->mxCardType = X400PCARDTYPE_CP200P;
		break;
	case CP200E:
		val->mxCardType = X400PCARDTYPE_CP200E;
		break;
	case CP400:
		val->mxCardType = X400PCARDTYPE_CP400;
		break;
	case CP400P:
		val->mxCardType = X400PCARDTYPE_CP400P;
		break;
	case CP400E:
		val->mxCardType = X400PCARDTYPE_CP400E;
		break;
	default:
		val->mxCardType = X400PCARDTYPE_NONE;
		break;
	}
	/* this driver does not support board indexes yet */
	val->mxCardIdentifier = 0;
	/* this driver does not support board revision yet */
	val->mxCardRevision = 0;
	switch (cd->device) {
	case XP_DEV_DS2152:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS2152;
		break;
	case XP_DEV_DS21352:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS21352;
		break;
	case XP_DEV_DS21552:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS21552;
		break;
	case XP_DEV_DS2154:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS2154;
		break;
	case XP_DEV_DS21354:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS21354;
		break;
	case XP_DEV_DS21554:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS21554;
		break;
	case XP_DEV_DS2155:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS2155;
		break;
	case XP_DEV_DS21455:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS21455;
		break;
	case XP_DEV_DS21458:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS21458;
		break;
	case XP_DEV_DS2156:
		val->mxCardChipType = X400PCARDCHIPTYPE_DS2156;
		break;
	default:
		val->mxCardChipType = X400PCARDCHIPTYPE_NONE;
		break;
	}
	val->mxCardChipRevision = cd->devrev;
	val->mxCardPciBus = cd->bus;
	val->mxCardPciSlot = cd->slot;
	val->mxCardPciIrq = cd->irq;
	strncpy(val->mxCardName,xp_board_info[cd->board].name,sizeof(val->mxCardName));
	return (0);
}

noinline __unlikely int
mx_iocginfo_span(struct sp *sp, mx_info_t * arg)
{
	/* there is no span information */
	return (0);
}

noinline __unlikely int
mx_iocginfo_chan(struct sp *sp, struct ch *ch, mx_info_t * arg)
{
	/* there is no channel information */
	return (0);
}

noinline __unlikely int
mx_iocginfo(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_info_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;
	psw_t flags;

	if ((ret = mx_info_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocginfo_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocginfo_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocginfo_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocginfo_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocginfo_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* this driver does not support fractionals */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* this driver does not support cross-connects */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* this driver does not support bert */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (ret);
}

STATIC __unlikely int
mx_option_size(mx_option_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->option->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->option->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->option->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->option->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->option->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->option->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->option->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->option->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgoption_dflt(mx_option_t * arg)
{
	/* this is no default options */
	return (0);
}

noinline __unlikely int
mx_iocgoption_sync(struct sg *sg, mx_option_t * arg)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
mx_iocgoption_card(struct cd *cd, mx_option_t * arg)
{
	struct mx_opt_conf_card *val = (typeof(val)) (arg + 1);

	val->mxCardMode = 0;
	if (cd->config.ifgmode & SDL_GMODE_LOC_LB)
		val->mxCardMode |= (1 << MXCARDMODE_LOCALLOOPBACK);
	if (cd->config.ifgmode & SDL_GMODE_REM_LB)
		val->mxCardMode |= (1 << MXCARDMODE_REMOTELOOPBACK);
	if (cd->config.ifclock == SDL_CLOCK_MASTER)
		val->mxCardSyncMaster = MXCARDSYNCMASTER_MASTER;
	else
		val->mxCardSyncMaster = MXCARDSYNCMASTER_SLAVE;
	switch (cd->config.ifsync) {
	default:
	case SYNCSELF:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNCSELF;
		break;
	case SYNC1:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNC1;
		break;
	case SYNC2:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNC2;
		break;
	case SYNC3:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNC3;
		break;
	case SYNC4:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNC4;
		break;
	case SYNCEXTERN:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNCEXTERN;
		break;
	case SYNCAUTO:
		val->mxCardSyncSource = MXCARDSYNCSOURCE_SYNCAUTO;
		break;
	}
	return (0);
}

noinline __unlikely int
mx_iocgoption_span(struct sp *sp, mx_option_t * arg)
{
	struct mx_opt_conf_span *val = (typeof(val)) (arg + 1);

	switch (sp->config.ifgmode) {
	case SDL_GMODE_LOC_LB:
		val->mxSpanMode = (1 << MXSPANMODE_LOCAL);
		break;
	case SDL_GMODE_REM_LB:
		val->mxSpanMode = (1 << MXSPANMODE_REMOTE);
		break;
	case SDL_GMODE_BOTH_LB:
		val->mxSpanMode = (1 << MXSPANMODE_LOCAL) | (1 << MXSPANMODE_REMOTE);
	default:
	case SDL_GMODE_NONE:
		val->mxSpanMode = 0;
		break;
	}
	switch (sp->config.ifclock) {
	case SDL_CLOCK_INT:
		val->mxSpanClocking = MXSPANCLOCKING_INTERNAL;
		break;
	case SDL_CLOCK_EXT:
		val->mxSpanClocking = MXSPANCLOCKING_EXTERNAL;
		break;
	case SDL_CLOCK_LOOP:
		val->mxSpanClocking = MXSPANCLOCKING_LOOP;
		break;
	case SDL_CLOCK_MASTER:
		val->mxSpanClocking = MXSPANCLOCKING_INTERNAL;
		break;
	case SDL_CLOCK_SLAVE:
		val->mxSpanClocking = MXSPANCLOCKING_EXTERNAL;
		break;
	default:
	case SDL_CLOCK_DPLL:
	case SDL_CLOCK_ABR:
	case SDL_CLOCK_SHAPER:
	case SDL_CLOCK_TICK:
	case SDL_CLOCK_NONE:
		val->mxSpanClocking = MXSPANCLOCKING_NONE;
		break;
	}
	if (sp->config.iftxlevel & ~0x7) {
		/* monitoring mode */
		val->mxSpanLineMode = MXSPANLINEMODE_MONITOR;
		val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
		val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_NONE;
		val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED120OHM;
		switch (sp->cd->device) {
		case XP_DEV_DS2155:
		case XP_DEV_DS21455:
		case XP_DEV_DS21458:
		case XP_DEV_DS2156:
		default:
			switch (sp->config.iftxlevel & 0x3) {
			case 0:	/* 0 */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
				break;
			case 1:	/* 20 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
				break;
			case 2:	/* 26 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON26DB;
				break;
			case 3:	/* 32 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON32DB;
				break;
			}
			break;
		case XP_DEV_DS2152:
		case XP_DEV_DS21352:
		case XP_DEV_DS21552:
			switch (sp->config.iftxlevel & 0x3) {
			case 0:	/* 0 */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
				break;
			case 1:	/* 12 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON12DB;
				break;
			case 2:
			case 3:	/* 20 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
				break;
			}
			break;
		case XP_DEV_DS2154:
		case XP_DEV_DS21354:
		case XP_DEV_DS21554:
			switch (sp->config.iftxlevel & 0x3) {
			case 0:	/* 0 */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
				break;
			case 1:	/* 12 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON12DB;
				break;
			case 2:
			case 3:	/* 30 dB */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON30DB;
				break;
			}
			break;
		}
	} else {
		/* normal DSU/CSU mode */
		val->mxSpanLineGain = MXSPANLINEGAIN_NONE;
		if (sp->config.ifgtype == SDL_GTYPE_E1) {
			val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
			val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_NONE;
			switch (sp->config.iftxlevel & 0x7) {
			case SDL_TXLEVEL_75OHM_NM:	/* 1 */
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
				val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_UNBALANCED75OHM;
				break;
			case SDL_TXLEVEL_75OHM_PR:	/* 3 */
			case SDL_TXLEVEL_75OHM_HRL:	/* 5 */
			case 7:
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
				val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_UNBALANCED75OHM;
				break;
			case 0:
			case SDL_TXLEVEL_120OHM_NM:	/* 2 */
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
				val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED120OHM;
				break;
			case SDL_TXLEVEL_120OHM_PR:	/* 4 */
			case SDL_TXLEVEL_120OHM_HRL:	/* 6 */
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
				val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED120OHM;
				break;
			}
		} else {
			val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED100OHM;
			switch (sp->config.iftxlevel & 0x7) {
			case 0:
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX133FT;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				break;
			case 1:
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX266FT;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				break;
			case 2:
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX399FT;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				break;
			case 3:
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX533FT;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				break;
			case 4:
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX666FT;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				break;
			case 5:
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU8DB;
				break;
			case 6:
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU15DB;
				break;
			case 7:
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
				val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU23DB;
				break;
			}
		}
	}
	val->mxSpanLineDelay = 0;	/* not measured this driver */
	val->mxSpanTxLevel = MXSPANTXLEVEL_ON;
	val->mxSpanRxLevel = MXSPANRXLEVEL_ON;
	val->mxSpanAlarmSettleTime = 5000;	/* always 5 seconds for this driver */
	val->mxSpanLineCodeTime = 5000;	/* always 5 seconds for this driver */
	val->mxSpanLineCode = MXSPANLINECODE_NOCODE;	/* not supported this driver */
	val->mxSpanReceiveThreshold = 0;	/* not supported this driver */
	return (0);
}

noinline __unlikely int
mx_iocgoption_chan(struct sp *sp, struct ch *ch, mx_option_t * arg)
{
	struct mx_opt_conf_chan *val;
	sdl_config_t *config;

	val = (typeof(val)) (arg + 1);
	config = ch ? &ch->sdl.config : &sp->config;
	switch (config->iftype) {
	case SDL_TYPE_DS0:	/* DS0 channel */
		val->mxChanType = (config->ifframing == SDL_FRAMING_CAS) ? MXCHANTYPE_CAS : MXCHANTYPE_CCS;
		val->mxChanFormat = MXCHANFORMAT_DS0;
		val->mxChanRate = MXCHANRATE_KBITS64;
		val->mxChanMode = 0;	/* no loopback supported */
		break;
	case SDL_TYPE_DS0A:	/* DS0A channel */
		val->mxChanType = (config->ifgtype == SDL_GTYPE_E1
				   && config->ifframing != MXCHANTYPE_CAS) ? MXCHANTYPE_CCS : MXCHANTYPE_CAS;
		val->mxChanFormat = MXCHANFORMAT_DS0A;
		val->mxChanRate = MXCHANRATE_KBITS56;
		val->mxChanMode = 0;	/* no loopback supported */
		break;
	case SDL_TYPE_E1:	/* full E1 span */
		val->mxChanType = (config->ifframing == SDL_FRAMING_CAS) ? MXCHANTYPE_CAS : MXCHANTYPE_CCS;
		val->mxChanFormat = MXCHANFORMAT_E1;
		val->mxChanRate = MXCHANRATE_KBITS1984;
		val->mxChanMode = 0;	/* no loopback supported */
		break;
	case SDL_TYPE_T1:	/* full T1 span */
		val->mxChanType = MXCHANTYPE_CCS;
		val->mxChanFormat = MXCHANFORMAT_T1;
		val->mxChanRate = MXCHANRATE_KBITS1536;
		val->mxChanMode = 0;	/* no loopback supported */
		break;
	case SDL_TYPE_J1:	/* full J1 span */
		val->mxChanType = MXCHANTYPE_CCS;
		val->mxChanFormat = MXCHANFORMAT_J1;
		val->mxChanRate = MXCHANRATE_KBITS1536;
		val->mxChanMode = 0;	/* no loopback supported */
		break;
	default:
		val->mxChanType = MXCHANTYPE_NONE;
		val->mxChanFormat = MXCHANFORMAT_NONE;
		val->mxChanRate = MXCHANRATE_VARIABLE;
		val->mxChanMode = 0;	/* no loopback supported */
		break;
	}
	return (0);
}

noinline __unlikely int
mx_iocgoption(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_option_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;
	psw_t flags;

	if ((ret = mx_option_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgoption_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgoption_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgoption_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgoption_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgoption_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (ret);
}

noinline __unlikely int
mx_test_options_dflt(mx_option_t * arg)
{
	if (arg->id != 0)
		goto esrch;
	/* there are no default options yet */
	return (0);
      esrch:
	return (-ESRCH);
}

noinline __unlikely int
mx_test_options_card(mx_option_t * arg)
{
	struct cd *cd;
	struct mx_opt_conf_card *val = (typeof(val)) (arg + 1);

	if (!(cd = cd_find(arg->id)))
		goto esrch;
	/* check validity of arguments */
	if (val->mxCardMode & ~((1 << MXCARDMODE_LOCALLOOPBACK) | (1 << MXCARDMODE_REMOTELOOPBACK)))
		goto einval;
	switch (val->mxCardSyncMaster) {
	case MXCARDSYNCMASTER_MASTER:
	case MXCARDSYNCMASTER_SLAVE:
		break;
	default:
		goto einval;
	}
	switch (val->mxCardSyncSource) {
	case MXCARDSYNCSOURCE_SYNCSELF:
	case MXCARDSYNCSOURCE_SYNC1:
	case MXCARDSYNCSOURCE_SYNC2:
	case MXCARDSYNCSOURCE_SYNC3:
	case MXCARDSYNCSOURCE_SYNC4:
		break;
	default:
	case MXCARDSYNCSOURCE_SYNCEXTERN:
	case MXCARDSYNCSOURCE_SYNCAUTO:
		goto einval;
	}
	return (0);
      esrch:
	return (-ESRCH);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_test_options_span(mx_option_t * arg)
{
	struct sp *sp;
	struct mx_opt_conf_span *val = (typeof(val)) (arg + 1);

	if (!(sp = sp_find(arg->id)))
		goto esrch;
	/* check validity of values */
	if (val->mxSpanMode & ~((1 << MXSPANMODE_LOCAL) | (1 << MXSPANMODE_REMOTE)))
		goto einval;
	switch (val->mxSpanClocking) {
	case MXSPANCLOCKING_INTERNAL:
	case MXSPANCLOCKING_EXTERNAL:
	case MXSPANCLOCKING_LOOP:
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanLineMode) {
	case MXSPANLINEMODE_NONE:
		/* set to whatever it is */
		if (sp->config.iftxlevel & ~0x7) {
			val->mxSpanLineMode = MXSPANLINEMODE_MONITOR;
		} else if (sp->config.ifgtype == SDL_GTYPE_E1) {
			if (sp->config.iftxlevel & 0x6)
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
			else
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
		} else {
			if (sp->config.iftxlevel == 0 || sp->config.iftxlevel > 4)
				val->mxSpanLineMode = MXSPANLINEMODE_CSU;
			else
				val->mxSpanLineMode = MXSPANLINEMODE_DSU;
		}
		break;
	case MXSPANLINEMODE_DSU:
	case MXSPANLINEMODE_CSU:
	case MXSPANLINEMODE_MONITOR:
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanLineLength) {
	case MXSPANLINELENGTH_NONE:
		/* set to whatever it is */
		if (sp->config.ifgtype != SDL_GTYPE_E1 && sp->config.iftxlevel < 5) {
			switch (sp->config.iftxlevel) {
			case 0:
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX133FT;
				break;
			case 1:
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX266FT;
				break;
			case 2:
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX399FT;
				break;
			case 3:
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX533FT;
				break;
			case 4:
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX666FT;
				break;
			}
		}
		break;
	case MXSPANLINELENGTH_DSX133FT:
	case MXSPANLINELENGTH_DSX266FT:
	case MXSPANLINELENGTH_DSX399FT:
	case MXSPANLINELENGTH_DSX533FT:
	case MXSPANLINELENGTH_DSX666FT:
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanLineAttenuation) {
	case MXSPANLINEATTENUATION_NONE:
		/* set it to whatever it is */
		if (sp->config.ifgtype != SDL_GTYPE_E1 && sp->config.iftxlevel < 8) {
			switch (sp->config.iftxlevel) {
			case 0:
			case 1:
			case 2:
			case 3:
			case 4:
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				break;
			case 5:
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU8DB;
				break;
			case 6:
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU15DB;
				break;
			case 7:
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU23DB;
				break;
			}
		}
		break;
	case MXSPANLINEATTENUATION_CSU0DB:
	case MXSPANLINEATTENUATION_CSU8DB:
	case MXSPANLINEATTENUATION_CSU15DB:
	case MXSPANLINEATTENUATION_CSU23DB:
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanLineGain) {
	case MXSPANLINEGAIN_NONE:
		/* set it to whatever it is */
		if (sp->config.iftxlevel >= 8) {
			switch (sp->cd->device) {
			case XP_DEV_DS2155:
			case XP_DEV_DS21455:
			case XP_DEV_DS21458:
			case XP_DEV_DS2156:
			default:
				switch (sp->config.iftxlevel & 0x3) {
				case 0:	/* 0 */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
					break;
				case 1:	/* 20 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
					break;
				case 2:	/* 26 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON26DB;
					break;
				case 3:	/* 32 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON32DB;
					break;
				}
				break;
			case XP_DEV_DS2152:
			case XP_DEV_DS21352:
			case XP_DEV_DS21552:
				switch (sp->config.iftxlevel & 0x3) {
				case 0:	/* 0 */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
					break;
				case 1:	/* 12 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON12DB;
					break;
				case 2:
				case 3:	/* 20 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
					break;
				}
				break;
			case XP_DEV_DS2154:
			case XP_DEV_DS21354:
			case XP_DEV_DS21554:
				switch (sp->config.iftxlevel & 0x3) {
				case 0:	/* 0 */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
					break;
				case 1:	/* 12 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON12DB;
					break;
				case 2:
				case 3:	/* 30 dB */
					val->mxSpanLineGain = MXSPANLINEGAIN_MON30DB;
					break;
				}
				break;
			}
		}
		break;
	case MXSPANLINEGAIN_MON0DB:
	case MXSPANLINEGAIN_MON12DB:
	case MXSPANLINEGAIN_MON20DB:
	case MXSPANLINEGAIN_MON26DB:
	case MXSPANLINEGAIN_MON30DB:
	case MXSPANLINEGAIN_MON32DB:
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanLineImpedance) {
	case MXSPANLINEIMPEDANCE_NONE:
		/* set it to whatever it is */
		if (sp->config.ifgtype == SDL_GTYPE_E1) {
			if (sp->config.iftxlevel & 0x1)
				val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_UNBALANCED75OHM;
			else
				val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED120OHM;
		} else {
			val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED100OHM;
		}
		break;
	case MXSPANLINEIMPEDANCE_UNBALANCED75OHM:
	case MXSPANLINEIMPEDANCE_BALANCED100OHM:
	case MXSPANLINEIMPEDANCE_BALANCED120OHM:
		break;
	default:
		goto einval;
	}
	if (val->mxSpanLineDelay != 0)
		goto einval;
	switch (val->mxSpanTxLevel) {
	case MXSPANTXLEVEL_OFF:
	case MXSPANTXLEVEL_ON:
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanRxLevel) {
	case MXSPANRXLEVEL_OFF:
	case MXSPANRXLEVEL_ON:
		break;
	default:
		goto einval;
	}
	if (val->mxSpanAlarmSettleTime != 5000)
		val->mxSpanAlarmSettleTime = 5000;
	if (val->mxSpanLineCodeTime != 5000)
		val->mxSpanLineCodeTime = 5000;
	switch (val->mxSpanLineCode) {
	case MXSPANLINECODE_NOCODE:
		break;
	default:
	case MXSPANLINECODE_LINECODE:
	case MXSPANLINECODE_PAYLOADCODE:
	case MXSPANLINECODE_RESETCODE:
	case MXSPANLINECODE_TESTCODE:
	case MXSPANLINECODE_UNFRAMEDALLONES:
	case MXSPANLINECODE_UNFRAMEDONEANDZERO:
		goto einval;
	}
	if (val->mxSpanReceiveThreshold != 0)
		goto einval;
	return (0);
      esrch:
	return (-ESRCH);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_test_options_chan(mx_option_t * arg)
{
	struct ch *ch;
	struct mx_opt_conf_chan *val = (typeof(val)) (arg + 1);

	if (IS_ERR((ch = ch_find(arg->id))) || ch == NULL)
		goto esrch;
	/* check validity of values */
	switch (val->mxChanType) {
	case MXCHANTYPE_NONE:
		val->mxChanType = MXCHANTYPE_CCS;
	case MXCHANTYPE_CCS:
		break;
	case MXCHANTYPE_CAS:
	default:
		goto einval;
	}
	switch (val->mxChanFormat) {
	case MXCHANFORMAT_NONE:
		/* set to what it is */
		switch (ch->sdl.config.iftype) {
		case SDL_TYPE_DS0:
			val->mxChanFormat = MXCHANFORMAT_DS0;
			break;
		case SDL_TYPE_DS0A:
			val->mxChanFormat = MXCHANFORMAT_DS0A;
			break;
		case SDL_TYPE_E1:
			val->mxChanFormat = MXCHANFORMAT_E1;
			break;
		case SDL_TYPE_T1:
			val->mxChanFormat = MXCHANFORMAT_T1;
			break;
		case SDL_TYPE_J1:
			val->mxChanFormat = MXCHANFORMAT_J1;
			break;
		}
		break;
	case MXCHANFORMAT_DS0:
	case MXCHANFORMAT_DS0A:
		break;
	case MXCHANFORMAT_E1:
		if (ch->sdl.config.ifgtype != SDL_GTYPE_E1)
			goto einval;
		break;
	case MXCHANFORMAT_T1:
		if (ch->sdl.config.ifgtype != SDL_GTYPE_T1)
			goto einval;
		break;
	case MXCHANFORMAT_J1:
		if (ch->sdl.config.ifgtype != SDL_GTYPE_J1)
			goto einval;
		break;
	default:
		goto einval;
	}
	switch (val->mxChanRate) {
	case MXCHANRATE_VARIABLE:
		/* set it to what it will be */
		switch (val->mxChanFormat) {
		case MXCHANFORMAT_DS0:
			val->mxChanRate = MXCHANRATE_KBITS64;
			break;
		case MXCHANFORMAT_DS0A:
			val->mxChanRate = MXCHANRATE_KBITS56;
			break;
		case MXCHANFORMAT_E1:
			val->mxChanRate = MXCHANRATE_KBITS1984;
			break;
		case MXCHANFORMAT_T1:
			val->mxChanRate = MXCHANRATE_KBITS1536;
			break;
		case MXCHANFORMAT_J1:
			val->mxChanRate = MXCHANRATE_KBITS1536;
			break;
		}
		break;
	case MXCHANRATE_KBITS56:
	case MXCHANRATE_KBITS64:
		break;
	case MXCHANRATE_KBITS1536:
		if (ch->sdl.config.ifgtype == SDL_GTYPE_E1)
			goto einval;
		break;
	case MXCHANRATE_KBITS1984:
		if (ch->sdl.config.ifgtype != SDL_GTYPE_E1)
			goto einval;
		break;
	default:
		goto einval;
	}
	switch (val->mxChanMode) {
	case (1 << MXCHANMODE_REMOTELOOPBACK):
	case (1 << MXCHANMODE_LOCALLOOPBACK):
	case (1 << MXCHANMODE_TEST):
	case (1 << MXCHANMODE_LOOPBACKECHO):
		break;
	default:
		goto einval;
	}
	return (0);
      esrch:
	return (-ESRCH);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_test_options(queue_t *q, mblk_t *mp, mx_option_t * arg)
{
	int ret;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if ((unsigned char *) (&arg->option->dflt + 1) > mp->b_cont->b_wptr)
			goto emsgsize;
		if ((ret = mx_test_options_dflt(arg)) >= 0)
			mp->b_cont->b_wptr = (unsigned char *) (&arg->option->dflt + 1);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		goto esrch;
	case MX_OBJ_TYPE_CARD:	/* card */
		if ((unsigned char *) (&arg->option->card + 1) > mp->b_cont->b_wptr)
			goto emsgsize;
		if ((ret = mx_test_options_card(arg)) >= 0)
			mp->b_cont->b_wptr = (unsigned char *) (&arg->option->card + 1);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if ((unsigned char *) (&arg->option->span + 1) > mp->b_cont->b_wptr)
			goto emsgsize;
		if ((ret = mx_test_options_span(arg)) >= 0)
			mp->b_cont->b_wptr = (unsigned char *) (&arg->option->span + 1);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if ((unsigned char *) (&arg->option->chan + 1) > mp->b_cont->b_wptr)
			goto emsgsize;
		if ((ret = mx_test_options_chan(arg)) >= 0)
			mp->b_cont->b_wptr = (unsigned char *) (&arg->option->chan + 1);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		goto esrch;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		goto esrch;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		goto esrch;
	default:
		goto einval;
	}
	return (ret);
      emsgsize:
	return (-EMSGSIZE);
      esrch:
	return (-ESRCH);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_set_options_dflt(mx_option_t * arg)
{
	/* there are no default options yet */
	return (0);
}

noinline __unlikely int
mx_set_options_sync(struct sg *sg, mx_option_t * arg)
{
	return (0);
}

noinline __unlikely int
mx_set_options_card(struct cd *cd, mx_option_t * arg)
{
	struct mx_opt_conf_card *val = (typeof(val)) (arg + 1);
	uint card_reconfig = 0;

	/* validity of options already checked */
	if (val->mxCardMode & (1 << MXCARDMODE_LOCALLOOPBACK)) {
		if (!(cd->config.ifgmode & SDL_GMODE_LOC_LB)) {
			card_reconfig = 1;
			cd->config.ifgmode |= SDL_GMODE_LOC_LB;
		}
	}
	if (val->mxCardMode & (1 << MXCARDMODE_REMOTELOOPBACK)) {
		if (!(cd->config.ifgmode & SDL_GMODE_REM_LB)) {
			card_reconfig = 1;
			cd->config.ifgmode |= SDL_GMODE_REM_LB;
		}
	}
	switch (val->mxCardSyncMaster) {
	case MXCARDSYNCMASTER_MASTER:
		if (cd->config.ifclock != SDL_CLOCK_MASTER) {
			card_reconfig = 1;
			cd->config.ifclock = SDL_CLOCK_MASTER;
		}
		break;
	case MXCARDSYNCMASTER_SLAVE:
		if (cd->config.ifclock != SDL_CLOCK_SLAVE) {
			card_reconfig = 1;
			cd->config.ifclock = SDL_CLOCK_SLAVE;
		}
		break;
	}
	switch (val->mxCardSyncSource) {
	case MXCARDSYNCSOURCE_SYNCSELF:
		if (cd->config.ifsync != SYNCSELF) {
			card_reconfig = 1;
			cd->config.ifsync = SYNCSELF;
		}
		break;
	case MXCARDSYNCSOURCE_SYNC1:
		if (cd->config.ifsync != SYNC1) {
			card_reconfig = 1;
			cd->config.ifsync = SYNC1;
		}
		break;
	case MXCARDSYNCSOURCE_SYNC2:
		if (cd->config.ifsync != SYNC2) {
			card_reconfig = 1;
			cd->config.ifsync = SYNC2;
		}
		break;
	case MXCARDSYNCSOURCE_SYNC3:
		if (cd->config.ifsync != SYNC3) {
			card_reconfig = 1;
			cd->config.ifsync = SYNC3;
		}
		break;
	case MXCARDSYNCSOURCE_SYNC4:
		if (cd->config.ifsync != SYNC4) {
			card_reconfig = 1;
			cd->config.ifsync = SYNC4;
		}
		break;
	case MXCARDSYNCSOURCE_SYNCEXTERN:
		if (cd->config.ifsync != SYNCEXTERN) {
			card_reconfig = 1;
			cd->config.ifsync = SYNCEXTERN;
		}
		break;
	case MXCARDSYNCSOURCE_SYNCAUTO:
		if (cd->config.ifsync != SYNCAUTO) {
			card_reconfig = 1;
			cd->config.ifsync = SYNCAUTO;
		}
		break;
	}
	if (card_reconfig && (cd->config.ifflags & SDL_IF_UP))
		xp_card_reconfig(cd, 0);
	return (0);
}

noinline __unlikely int
mx_set_options_span(struct sp *sp, mx_option_t * arg)
{
	struct mx_opt_conf_span *val = (typeof(val)) (arg + 1);
	uint txlevel = 0, span_reconfig = 0;

	/* mxSpanMode */
	if (val->mxSpanMode & (1 << MXSPANMODE_LOCAL)) {
		if (!(sp->config.ifgmode & SDL_GMODE_LOC_LB)) {
			span_reconfig = 1;
			sp->config.ifgmode |= SDL_GMODE_LOC_LB;
		}
	} else {
		if (sp->config.ifgmode & SDL_GMODE_LOC_LB) {
			span_reconfig = 1;
			sp->config.ifgmode &= ~SDL_GMODE_LOC_LB;
		}
	}
	if (val->mxSpanMode & (1 << MXSPANMODE_REMOTE)) {
		if (!(sp->config.ifgmode & SDL_GMODE_REM_LB)) {
			span_reconfig = 1;
			sp->config.ifgmode |= SDL_GMODE_REM_LB;
		}
	} else {
		if (sp->config.ifgmode & SDL_GMODE_REM_LB) {
			span_reconfig = 1;
			sp->config.ifgmode &= ~SDL_GMODE_REM_LB;
		}
	}
	/* mxSpanClocking */
	switch (val->mxSpanClocking) {
	case MXSPANCLOCKING_INTERNAL:
		if (sp->config.ifclock != SDL_CLOCK_INT) {
			span_reconfig = 1;
			sp->config.ifclock = SDL_CLOCK_INT;
		}
		break;
	case MXSPANCLOCKING_EXTERNAL:
		if (sp->config.ifclock != SDL_CLOCK_EXT) {
			span_reconfig = 1;
			sp->config.ifclock = SDL_CLOCK_EXT;
		}
		break;
	case MXSPANCLOCKING_LOOP:
		if (sp->config.ifclock != SDL_CLOCK_LOOP) {
			span_reconfig = 1;
			sp->config.ifclock = SDL_CLOCK_LOOP;
		}
		break;
	}
	/* mxSpanLineMode */
	/* mxSpanLineImpedance */
	/* mxSpanLineLength */
	/* mxSpanLineAttenuation */
	/* mxSpanLineGain */
	switch (val->mxSpanLineMode) {
	case MXSPANLINEMODE_MONITOR:
		val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
		val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_NONE;
		val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_NONE;
		txlevel = 8;
		switch (val->mxSpanLineGain) {
		case MXSPANLINEGAIN_NONE:
		case MXSPANLINEGAIN_MON0DB:
			break;
		case MXSPANLINEGAIN_MON12DB:
			switch (sp->cd->device) {
			case XP_DEV_DS2155:
			case XP_DEV_DS21455:
			case XP_DEV_DS21458:
			case XP_DEV_DS2156:
				txlevel += 0;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON0DB;
				break;
			default:
				txlevel += 1;
				break;
			}
			break;
		case MXSPANLINEGAIN_MON20DB:
			switch (sp->cd->device) {
			case XP_DEV_DS2155:
			case XP_DEV_DS21455:
			case XP_DEV_DS21458:
			case XP_DEV_DS2156:
				txlevel += 1;
				break;
			case XP_DEV_DS2152:
			case XP_DEV_DS21352:
			case XP_DEV_DS21552:
				txlevel += 2;
				break;
			case XP_DEV_DS2154:
			case XP_DEV_DS21354:
			case XP_DEV_DS21554:
				txlevel += 1;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON12DB;
				break;
			}
			break;
		case MXSPANLINEGAIN_MON26DB:
			switch (sp->cd->device) {
			case XP_DEV_DS2155:
			case XP_DEV_DS21455:
			case XP_DEV_DS21458:
			case XP_DEV_DS2156:
				txlevel += 2;
				break;
			case XP_DEV_DS2152:
			case XP_DEV_DS21352:
			case XP_DEV_DS21552:
				txlevel += 2;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
				break;
			case XP_DEV_DS2154:
			case XP_DEV_DS21354:
			case XP_DEV_DS21554:
				txlevel += 1;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON12DB;
				break;
			}
			break;
		case MXSPANLINEGAIN_MON30DB:
			switch (sp->cd->device) {
			case XP_DEV_DS2155:
			case XP_DEV_DS21455:
			case XP_DEV_DS21458:
			case XP_DEV_DS2156:
				txlevel += 2;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON26DB;
				break;
			case XP_DEV_DS2152:
			case XP_DEV_DS21352:
			case XP_DEV_DS21552:
				txlevel += 2;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
				break;
			case XP_DEV_DS2154:
			case XP_DEV_DS21354:
			case XP_DEV_DS21554:
				txlevel += 2;
				break;
			}
			break;
		case MXSPANLINEGAIN_MON32DB:
			switch (sp->cd->device) {
			case XP_DEV_DS2155:
			case XP_DEV_DS21455:
			case XP_DEV_DS21458:
			case XP_DEV_DS2156:
				txlevel += 3;
				break;
			case XP_DEV_DS2152:
			case XP_DEV_DS21352:
			case XP_DEV_DS21552:
				txlevel += 2;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON20DB;
				break;
			case XP_DEV_DS2154:
			case XP_DEV_DS21354:
			case XP_DEV_DS21554:
				txlevel += 2;
				/* nearest lower */
				val->mxSpanLineGain = MXSPANLINEGAIN_MON30DB;
				break;
			}
			break;
		}
		break;
	case MXSPANLINEMODE_DSU:
		val->mxSpanLineGain = MXSPANLINEGAIN_NONE;
		val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_NONE;
		if (sp->config.ifgtype == SDL_GTYPE_E1) {
			val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
			val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_UNBALANCED75OHM;
			txlevel = 1;
		} else {
			val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED100OHM;
			switch (val->mxSpanLineLength) {
			case MXSPANLINELENGTH_NONE:
				val->mxSpanLineLength = MXSPANLINELENGTH_DSX133FT;
				/* fall through */
			case MXSPANLINELENGTH_DSX133FT:
				txlevel = 0;
				break;
			case MXSPANLINELENGTH_DSX266FT:
				txlevel = 1;
				break;
			case MXSPANLINELENGTH_DSX399FT:
				txlevel = 2;
				break;
			case MXSPANLINELENGTH_DSX533FT:
				txlevel = 3;
				break;
			case MXSPANLINELENGTH_DSX666FT:
				txlevel = 4;
				break;
			}
		}
		break;
	case MXSPANLINEMODE_CSU:
		val->mxSpanLineGain = MXSPANLINEGAIN_NONE;
		val->mxSpanLineLength = MXSPANLINELENGTH_NONE;
		if (sp->config.ifgtype == SDL_GTYPE_E1) {
			val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED120OHM;
			val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_NONE;
			txlevel = 1;
		} else {
			val->mxSpanLineImpedance = MXSPANLINEIMPEDANCE_BALANCED100OHM;
			switch (val->mxSpanLineAttenuation) {
			case MXSPANLINEATTENUATION_NONE:
				val->mxSpanLineAttenuation = MXSPANLINEATTENUATION_CSU0DB;
				/* fall through */
			case MXSPANLINEATTENUATION_CSU0DB:
				txlevel = 0;
				break;
			case MXSPANLINEATTENUATION_CSU8DB:
				txlevel = 5;
				break;
			case MXSPANLINEATTENUATION_CSU15DB:
				txlevel = 6;
				break;
			case MXSPANLINEATTENUATION_CSU23DB:
				txlevel = 7;
				break;
			}
		}
		break;
	}
	if (sp->config.iftxlevel != txlevel) {
		span_reconfig = 1;
		sp->config.iftxlevel = txlevel;
	}
	/* mxSpanLineDelay */
	/* mxSpanTxLevel */
	switch (val->mxSpanTxLevel) {
	case MXSPANTXLEVEL_ON:
		if (!(sp->config.ifflags & SDL_IF_TX_RUNNING)) {
			span_reconfig = 1;
			sp->config.ifflags |= SDL_IF_TX_RUNNING;
		}
		break;
	case MXSPANTXLEVEL_OFF:
	case MXSPANTXLEVEL_OPEN:
		if (sp->config.ifflags & SDL_IF_TX_RUNNING) {
			span_reconfig = 1;
			sp->config.ifflags &= ~SDL_IF_TX_RUNNING;
		}
		break;
	}
	/* mxSpanRxLevel */
	switch (val->mxSpanRxLevel) {
	case MXSPANRXLEVEL_ON:
		if (!(sp->config.ifflags & SDL_IF_RX_RUNNING)) {
			span_reconfig = 1;
			sp->config.ifflags |= SDL_IF_RX_RUNNING;
		}
		break;
	case MXSPANRXLEVEL_OFF:
	case MXSPANRXLEVEL_OPEN:
		if (sp->config.ifflags & SDL_IF_RX_RUNNING) {
			span_reconfig = 1;
			sp->config.ifflags &= ~SDL_IF_RX_RUNNING;
		}
		break;
	}
	/* mxSpanLineAlarmSettleTime */
	/* mxSpanLineCodeTime */
	/* mxSpanLineCode */
	/* mxSpanReceiveThreshold */
	if (span_reconfig && (sp->config.ifflags & SDL_IF_UP))
		xp_span_reconfig(sp);
	return (0);
}

noinline __unlikely int
mx_set_options_chan(struct ch *ch, mx_option_t * arg)
{
	struct mx_opt_conf_chan *val = (typeof(val)) (arg + 1);
	uint chan_reconfig = 0;

	/* mxChanType */
	/* always MXCHANTYPE_CCS for this driver */
	/* mxChanFormat */
	switch (val->mxChanFormat) {
	case MXCHANFORMAT_DS0A:
		if (ch->sdl.config.iftype != SDL_TYPE_DS0A) {
			chan_reconfig = 1;
			ch->sdl.config.iftype = SDL_TYPE_DS0A;
		}
		break;
	case MXCHANFORMAT_DS0:
		if (ch->sdl.config.iftype != SDL_TYPE_DS0) {
			chan_reconfig = 1;
			ch->sdl.config.iftype = SDL_TYPE_DS0;
		}
		break;
	case MXCHANFORMAT_E1:
		if (ch->sdl.config.iftype != SDL_TYPE_E1) {
			chan_reconfig = 1;
			ch->sdl.config.iftype = SDL_TYPE_E1;
		}
		break;
	case MXCHANFORMAT_T1:
		if (ch->sdl.config.iftype != SDL_TYPE_T1) {
			chan_reconfig = 1;
			ch->sdl.config.iftype = SDL_TYPE_T1;
		}
		break;
	case MXCHANFORMAT_J1:
		if (ch->sdl.config.iftype != SDL_TYPE_J1) {
			chan_reconfig = 1;
			ch->sdl.config.iftype = SDL_TYPE_J1;
		}
		break;
	}
	switch (val->mxChanRate) {
	case MXCHANRATE_KBITS56:
		if (ch->sdl.config.ifrate != SDL_RATE_DS0A) {
			chan_reconfig = 1;
			ch->sdl.config.ifrate = SDL_RATE_DS0A;
		}
		break;
	case MXCHANRATE_KBITS64:
		if (ch->sdl.config.ifrate != SDL_RATE_DS0) {
			chan_reconfig = 1;
			ch->sdl.config.ifrate = SDL_RATE_DS0;
		}
		break;
	case MXCHANRATE_KBITS1536:
		if (ch->sdl.config.ifrate != SDL_RATE_T1) {
			chan_reconfig = 1;
			ch->sdl.config.ifrate = SDL_RATE_T1;
		}
		break;
	case MXCHANRATE_KBITS1984:
		if (ch->sdl.config.ifrate != SDL_RATE_E1) {
			chan_reconfig = 1;
			ch->sdl.config.ifrate = SDL_RATE_E1;
		}
		break;
	}
	switch (val->mxChanMode) {
	case (1 << MXCHANMODE_REMOTELOOPBACK):
		if (ch->sdl.config.ifmode != SDL_MODE_REM_LB) {
			chan_reconfig = 1;
			ch->sdl.config.ifmode = SDL_MODE_REM_LB;
		}
		break;
	case (1 << MXCHANMODE_LOCALLOOPBACK):
		if (ch->sdl.config.ifmode != SDL_MODE_LOC_LB) {
			chan_reconfig = 1;
			ch->sdl.config.ifmode = SDL_MODE_LOC_LB;
		}
		break;
	case (1 << MXCHANMODE_TEST):
		if (ch->sdl.config.ifmode != SDL_MODE_TEST) {
			chan_reconfig = 1;
			ch->sdl.config.ifmode = SDL_MODE_TEST;
		}
		break;
	case (1 << MXCHANMODE_LOOPBACKECHO):
		if (ch->sdl.config.ifmode != SDL_MODE_LB_ECHO) {
			chan_reconfig = 1;
			ch->sdl.config.ifmode = SDL_MODE_LB_ECHO;
		}
		break;
	}
	if (chan_reconfig && (ch->sdl.config.ifflags & SDL_IF_UP))
		/* FIXME */ ;
	return (0);
}

/** mx_iocsoption: - set options
  * @q: active queue (management stream write queue)
  * @mp: the input-output control
  */
noinline __unlikely int
mx_iocsoption(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_option_t *arg = (typeof(arg)) dp->b_rptr;
	int ret;

	ret = -ESRCH;

	switch (arg->type) {
		struct sg *sg;
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_set_options_dflt(arg);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_set_options_sync(sg, arg);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_set_options_card(cd, arg);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_set_options_span(sp, arg);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_set_options_chan(ch, arg);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return (ret);
}

/** mx_ioclconfig: - list configuration input-output control
  * @q: active queue (management stream write queue)
  * @mp: the input-output control
  *
  * Lists as a number of unsigned integers the identifiers of all of the elements of a type that
  * will fit into the buffer area.  When successful, returns the number of elements (whether they
  * would fit into the buffer or not).
  */
noinline __unlikely int
mx_ioclconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_config_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	uint num = 0, *val, can = arg->id;
	struct cd *cd;
	struct sp *sp;
	uint card, span, chan;
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(*hdr) + can * sizeof(*val), 0)))
		return (-ENOSR);
	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;
	val = (typeof(val)) (hdr + 1);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		/* list all default objects: there is always only one */
		if ((unsigned char *) (val + 1) <= db->b_wptr)
			*val = 0;
		val++;
		num++;
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* list all existing syncrhonization groups: this driver does not support syncrhonization
		   groups, so the list is always empty? XXX */
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		/* list all equipped card ids */
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			if ((unsigned char *) (val + 1) <= db->b_wptr)
				*val = (cd->card << 12);
			val++;
			num++;
		}
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* list all equipped span ids */
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++, val++, num++)
				if ((unsigned char *) (val + 1) <= db->b_wptr)
					*val = (cd->card << 12) | (span << 8);
		}
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		/* list all equipped channel ids */
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++) {
				if (!(sp = cd->spans[span]))
					continue;
				switch (sp->config.ifgtype) {
				case SDL_GTYPE_E1:
					for (chan = 1; chan <= 31; chan++, val++, num++)
						if ((unsigned char *) (val + 1) <= db->b_wptr)
							*val = (cd->card << 12) | (span << 8) | (chan << 0);
					break;
				case SDL_GTYPE_T1:
					for (chan = 1; chan <= 24; chan++, val++, num++)
						if ((unsigned char *) (val + 1) <= db->b_wptr)
							*val = (cd->card << 12) | (span << 8) | (chan << 0);
					break;
				case SDL_GTYPE_J1:
					for (chan = 1; chan <= 24; chan++, val++, num++)
						if ((unsigned char *) (val + 1) <= db->b_wptr)
							*val = (cd->card << 12) | (span << 8) | (chan << 0);
					break;
				}
			}
		}
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* list all existing fractional ids: this driver does not support fractional, so the list is
		   always empty */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* list all existing cross connects: this driver does not support cross-connect, so the list
		   is always empty */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* list all equipped span ids capable of bert testing: this driver does not support bert, so
		   the list is always empty */
		break;
	default:
		goto einval;
	}
	if (db->b_wptr > (unsigned char *) val)
		db->b_wptr = (unsigned char *) val;
	hdr->id = num > can ? can : num;
	mi_copy_set_rval(mp, num);
	return (0);
      einval:
	return (-EINVAL);
}

STATIC __unlikely int
mx_config_size(mx_config_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->config->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->config->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->config->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->config->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->config->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->config->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->config->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->config->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgconfig_dflt(mx_config_t * arg)
{
	/* there is no configuration for default */
	return (0);
}

noinline __unlikely int
mx_iocgconfig_sync(struct sg *sg, mx_config_t * arg)
{
	return (-EOPNOTSUPP);
}

/** mx_iocgconfig_card: - get configuration information for a card
  * @cd: card structure
  * @arg; the configuration structure
  */
noinline __unlikely int
mx_iocgconfig_card(struct cd *cd, mx_config_t * arg)
{
	struct mx_conf_card *val = (typeof(val)) (arg + 1);

	val->mxCardIndex = cd->card;
	switch (cd->config.ifgtype) {
	case SDL_GTYPE_E1:
		val->mxCardSpanType = MXCARDSPANTYPE_E1;
		break;
	case SDL_GTYPE_T1:
		val->mxCardSpanType = MXCARDSPANTYPE_T1;
		break;
	case SDL_GTYPE_J1:
		val->mxCardSpanType = MXCARDSPANTYPE_J1;
		break;
	default:
		val->mxCardSpanType = MXCARDSPANTYPE_NONE;
		break;
	}
	val->mxCardSyncGroup = 0;
	return (0);
}

/** mx_iocgconfig_span: - get configuration information for a span
  * @sp: span structure
  * @arg; the configuration structure
  */
noinline __unlikely int
mx_iocgconfig_span(struct sp *sp, mx_config_t * arg)
{
	struct mx_conf_span *val;

	val = (typeof(val)) (arg + 1);
	val->mxCardIndex = (arg->id >> 12) & 0xf;
	val->mxSpanIndex = (arg->id >> 8) & 0xf;
	val->mxSpanName[0] = '\0';
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		val->mxSpanType = MXSPANTYPE_E1;
		break;
	case SDL_GTYPE_T1:
		val->mxSpanType = MXSPANTYPE_T1;
		break;
	case SDL_GTYPE_J1:
		val->mxSpanType = MXSPANTYPE_J1;
		break;
	case SDL_GTYPE_E2:
		val->mxSpanType = MXSPANTYPE_E2;
		break;
	case SDL_GTYPE_E3:
		val->mxSpanType = MXSPANTYPE_E3;
		break;
	case SDL_GTYPE_T3:
		val->mxSpanType = MXSPANTYPE_T3;
		break;
	default:
		val->mxSpanType = MXSPANTYPE_NONE;
		break;
	}
	switch (sp->config.ifgcrc) {
	case SDL_GCRC_CRC4:
		val->mxSpanCrc = MXSPANCRC_CRC4;
		break;
	case SDL_GCRC_CRC5:
		val->mxSpanCrc = MXSPANCRC_CRC5;
		break;
	case SDL_GCRC_CRC6:
		val->mxSpanCrc = MXSPANCRC_CRC6;
		break;
	case SDL_GCRC_CRC6J:
		val->mxSpanCrc = MXSPANCRC_CRC6J;
		break;
	default:
	case SDL_GCRC_NONE:
		val->mxSpanCrc = MXSPANCRC_NONE;
		break;
	}
	switch (sp->config.ifcoding) {
	case SDL_CODING_AMI:
		val->mxSpanCoding = MXSPANCODING_AMI;
		break;
	case SDL_CODING_B6ZS:
		val->mxSpanCoding = MXSPANCODING_B6ZS;
		break;
	case SDL_CODING_B8ZS:
		val->mxSpanCoding = MXSPANCODING_B8ZS;
		break;
	case SDL_CODING_HDB3:
		val->mxSpanCoding = MXSPANCODING_HDB3;
		break;
	default:
	case SDL_CODING_NONE:
	case SDL_CODING_NRZ:
	case SDL_CODING_NRZI:
	case SDL_CODING_AAL1:
	case SDL_CODING_AAL2:
	case SDL_CODING_AAL5:
		val->mxSpanCoding = MXSPANCODING_NONE;
		break;
	}
	switch (sp->config.ifframing) {
	case SDL_FRAMING_CCS:
		val->mxSpanFraming = MXSPANFRAMING_CCS;
		break;
	case SDL_FRAMING_CAS:
		val->mxSpanFraming = MXSPANFRAMING_CAS;
		break;
	case SDL_FRAMING_SF:	/* Same as D4 */
		val->mxSpanFraming = MXSPANFRAMING_SF;
		break;
	case SDL_FRAMING_ESF:
		val->mxSpanFraming = MXSPANFRAMING_ESF;
		break;
	default:
	case SDL_FRAMING_NONE:
		val->mxSpanFraming = MXSPANFRAMING_NONE;
		break;
	}
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		val->mxSpanDataLink = MXSPANDATALINK_OTHER;
		break;
	case SDL_GTYPE_T1:
		val->mxSpanDataLink = MXSPANDATALINK_ANSIT1403;
		break;
	case SDL_GTYPE_J1:
		val->mxSpanDataLink = MXSPANDATALINK_OTHER;
		break;
	}
	val->mxSpanPriority = 0;
	val->mxSpanPrimary = 0;
	return (0);
}

/** mx_iocgconfig_chan: - get configuration information for a channel
  * @sp: span structure
  * @ch: channel structure
  * @arg; the configuration structure
  */
noinline __unlikely int
mx_iocgconfig_chan(struct sp *sp, struct ch *ch, mx_config_t * arg)
{
	struct mx_conf_chan *val;

	val = (typeof(val)) (arg + 1);
	val->mxCardIndex = (arg->id >> 12) & 0x0f;
	val->mxSpanIndex = (arg->id >> 8) & 0x0f;
	val->mxChanIndex = (arg->id >> 0) & 0xff;
	return (0);
}

/** mx_iocgconfig: - get configuration information for an object
  * @q: active queue (management stream write queue)
  * @mp: the input-output control
  * @arg; the configuration structure
  */
noinline __unlikely int
mx_iocgconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_config_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;
	psw_t flags;

	arg->cmd = MX_GET;

	if ((ret = mx_config_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgconfig_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgconfig_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgconfig_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgconfig_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgconfig_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (ret);
}

noinline __unlikely int
mx_test_config_dflt(mx_config_t * arg)
{
	return (0);
}

noinline __unlikely int
mx_test_config_sync(struct sg *sg, mx_config_t * arg)
{
	return (0);
}

noinline __unlikely int
mx_test_config_card(struct cd *cd, mx_config_t * arg)
{
	struct mx_conf_card *val = (typeof(val)) (arg + 1);

	if (val->mxCardIndex != cd->card)
		goto einval;
	switch (val->mxCardSpanType) {
	case MXSPANTYPE_E1:
		switch (cd->device) {
		case XP_DEV_DS2152:
		case XP_DEV_DS21352:
		case XP_DEV_DS21552:
			/* These chips do not support E1. */
			goto einval;
		}
		switch (cd->board) {
		case PLX9030:
		case PLXDEVBRD:
		case T400P:
		case T400PSS7:
		case V400PT:
		default:
			/* These cards do not support E1. */
			goto einval;
		case A400P:
		case V401PT:
		case A400PT:
			/* These cards actually suport E1. */
			break;
		case CP100: case CP100P: case CP100E:
		case CP200: case CP200P: case CP200E:
		case CP400: case CP400P: case CP400E:
			/* These cards suport E1. */
			break;
		}
		break;
	case MXSPANTYPE_T1:
	case MXSPANTYPE_J1:
		switch (cd->device) {
		case XP_DEV_DS2152:
			/* This chip does not support J1. */
			if (val->mxCardSpanType == MXSPANTYPE_J1)
				goto einval;
			break;
		case XP_DEV_DS2154:
		case XP_DEV_DS21354:
		case XP_DEV_DS21554:
			/* These chips do not support T1 or J1. */
			goto einval;
		}
		switch (cd->board) {
		case E400P:
		case E400PSS7:
		case V400PE:
			/* These cards do not support T1 or J1. */
			goto einval;
		case A400P:
		case V401PE:
		case A400PE:
			/* These cards actually support T1 or J1. */
			break;
		case CP100: case CP100P: case CP100E:
		case CP200: case CP200P: case CP200E:
		case CP400: case CP400P: case CP400E:
			/* These cards suport T1 or J1. */
			break;
		}
		break;
		break;
	default:
	case MXSPANTYPE_E2:
	case MXSPANTYPE_E3:
	case MXSPANTYPE_T3:
		/* these are not applicable */
		goto einval;
	case MXSPANTYPE_NONE:
		/* don't change modes at all */
		switch (cd->config.ifgtype) {
		case SDL_GTYPE_E1:
			val->mxCardSpanType = MXCARDSPANTYPE_E1;
			break;
		case SDL_GTYPE_T1:
			val->mxCardSpanType = MXCARDSPANTYPE_T1;
			break;
		case SDL_GTYPE_J1:
			val->mxCardSpanType = MXCARDSPANTYPE_J1;
			break;
		case SDL_GTYPE_E2:
			val->mxCardSpanType = MXCARDSPANTYPE_E1;
			break;
		case SDL_GTYPE_E3:
			val->mxCardSpanType = MXCARDSPANTYPE_E1;
			break;
		case SDL_GTYPE_T3:
			val->mxCardSpanType = MXCARDSPANTYPE_T1;
			break;
		default:
			val->mxCardSpanType = MXCARDSPANTYPE_NONE;
			break;
		}
		break;
	}
	/* the driver only supports one large default synchronization group */
	if (val->mxCardSyncGroup != 0)
		goto einval;
	return (0);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_test_config_span(struct sp *sp, mx_config_t * arg)
{
	struct cd *cd;
	struct mx_conf_span *val = (typeof(val)) (arg + 1);

	cd = sp->cd;
	if (val->mxCardIndex != cd->card)
		goto einval;
	if (val->mxSpanIndex != sp->span)
		goto einval;
	switch (val->mxSpanType) {
		/* affects configuration of channels */
	case MXSPANTYPE_NONE:
		/* leave it at whatever it is already set to */
		switch (sp->config.ifgtype) {
		case SDL_GTYPE_E1:
			val->mxSpanType = MXSPANTYPE_E1;
			break;
		case SDL_GTYPE_T1:
			val->mxSpanType = MXSPANTYPE_T1;
			break;
		case SDL_GTYPE_J1:
			val->mxSpanType = MXSPANTYPE_J1;
			break;
		}
		break;
	case MXSPANTYPE_T1:
	case MXSPANTYPE_J1:
		/* span cannot be different from board mode unless it is just the difference between T1 and
		   J1: is this really true? I think that the card's internal clock and external bus clock is
		   either E1 or T1 rate, however, there is nothing really stopping using self timing.  There
		   is even a way of using the jitter attenuator to run T1 off of an E1 clock, but simplify
		   things for now. */
		if (cd->config.ifgtype == SDL_GTYPE_E1)
			goto einval;
		break;
	case MXSPANTYPE_E1:
		/* span cannot be different from board mode unless it is just the difference between T1 and
		   J1: is this really true? I think that the card's internal clock and external bus clock is
		   either E1 or T1 rate, however, there is nothing really stopping using self timing.  There
		   is even a way of using the jitter attenuator to run T1 off of an E1 clock, but simplify
		   things for now. */
		if (cd->config.ifgtype != SDL_GTYPE_E1)
			goto einval;
		break;
	default:
	case MXSPANTYPE_E2:
	case MXSPANTYPE_E3:
	case MXSPANTYPE_T3:
		goto einval;
	}
	if (val->mxSpanCrc == MXSPANCRC_NONE) {
		switch (sp->config.ifgcrc) {
		case SDL_GCRC_CRC4:
			val->mxSpanCrc = MXSPANCRC_CRC4;
			break;
		case SDL_GCRC_CRC5:
			val->mxSpanCrc = MXSPANCRC_CRC5;
			break;
		case SDL_GCRC_CRC6:
			val->mxSpanCrc = MXSPANCRC_CRC6;
			break;
		case SDL_GCRC_CRC6J:
			val->mxSpanCrc = MXSPANCRC_CRC6J;
			break;
		}
	}
	if (val->mxSpanCoding == MXSPANCODING_NONE) {
		switch (sp->config.ifcoding) {
		case SDL_CODING_AMI:
			val->mxSpanCoding = MXSPANCODING_AMI;
			break;
		case SDL_CODING_B6ZS:
			val->mxSpanCoding = MXSPANCODING_B6ZS;
			break;
		case SDL_CODING_B8ZS:
			val->mxSpanCoding = MXSPANCODING_B8ZS;
			break;
		case SDL_CODING_HDB3:
			val->mxSpanCoding = MXSPANCODING_HDB3;
			break;
		}
	}
	if (val->mxSpanFraming == MXSPANFRAMING_NONE) {
		switch (sp->config.ifframing) {
		case SDL_FRAMING_CCS:
			val->mxSpanFraming = MXSPANFRAMING_CCS;
			break;
		case SDL_FRAMING_CAS:
			val->mxSpanFraming = MXSPANFRAMING_CAS;
			break;
		case SDL_FRAMING_SF:	/* SDL_FRAMING_D4 is the same */
			val->mxSpanFraming = MXSPANFRAMING_SF;
			break;
		case SDL_FRAMING_ESF:
			val->mxSpanFraming = MXSPANFRAMING_ESF;
			break;
		}
	}
	/* the setting of CRC must be consistent with mode and framing */
	switch (val->mxSpanCrc) {
	case MXSPANCRC_NONE:
		break;
	case MXSPANCRC_CRC4:	/* E1 CAS */
		if (val->mxSpanType != MXSPANTYPE_E1)
			goto einval;
		if (val->mxSpanFraming != MXSPANFRAMING_CAS)
			goto einval;
		break;
	case MXSPANCRC_CRC5:	/* E1 CCS */
		if (val->mxSpanType != MXSPANTYPE_E1)
			goto einval;
		if (val->mxSpanFraming != MXSPANFRAMING_CCS)
			goto einval;
		break;
	case MXSPANCRC_CRC6:	/* T1 */
		if (val->mxSpanType != MXSPANTYPE_T1)
			goto einval;
		break;
	case MXSPANCRC_CRC6J:	/* J1 */
		if (val->mxSpanType != MXSPANTYPE_J1)
			goto einval;
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanCoding) {
	case MXSPANCODING_NONE:
		break;
	case MXSPANCODING_AMI:
		/* ok for all modes and framing */
		break;
	case MXSPANCODING_B6ZS:
	case MXSPANCODING_B8ZS:
		if (val->mxSpanType == MXSPANTYPE_E1)
			goto einval;
		break;
	case MXSPANCODING_HDB3:
		if (val->mxSpanType != MXSPANTYPE_E1)
			goto einval;
		break;
	case MXSPANCODING_JBZS:
	case MXSPANCODING_ZBTSI:
		/* not supported by driver */
		goto einval;
	default:
		goto einval;
	}
	switch (val->mxSpanFraming) {
	case MXSPANFRAMING_NONE:
		break;
	case MXSPANFRAMING_CCS:
		if (val->mxSpanType != MXSPANTYPE_E1)
			goto einval;
		if (val->mxSpanCrc != MXSPANCRC_CRC5)
			goto einval;
		break;
	case MXSPANFRAMING_CAS:
		if (val->mxSpanType != MXSPANTYPE_E1)
			goto einval;
		if (val->mxSpanCrc != MXSPANCRC_CRC4)
			goto einval;
		break;
	case MXSPANFRAMING_SF:
		if (val->mxSpanType != MXSPANTYPE_T1)
			goto einval;
		if (val->mxSpanCrc != MXSPANCRC_CRC6)
			goto einval;
		break;
	case MXSPANFRAMING_D4:
		if (val->mxSpanType != MXSPANTYPE_T1)
			goto einval;
		if (val->mxSpanCrc != MXSPANCRC_CRC6)
			goto einval;
		break;
	case MXSPANFRAMING_ESF:
		if (val->mxSpanType != MXSPANTYPE_T1 && val->mxSpanType != MXSPANTYPE_J1)
			goto einval;
		if (val->mxSpanCrc != MXSPANCRC_CRC6 && val->mxSpanCrc != MXSPANCRC_CRC6J)
			goto einval;
		break;
	default:
		goto einval;
	}
	switch (val->mxSpanDataLink) {
	case MXSPANDATALINK_ANSIT1403:
		if (val->mxSpanType != MXSPANTYPE_T1 && val->mxSpanType != MXSPANTYPE_J1)
			goto einval;
		break;
	case MXSPANDATALINK_ATT54016:
		if (val->mxSpanType != MXSPANTYPE_T1)
			goto einval;
		break;
	case MXSPANDATALINK_OTHER:
		break;
	default:
		goto einval;
	}
	if (val->mxSpanPriority != 0) {
		if (1 > val->mxSpanPriority || val->mxSpanPriority > 4)
			goto einval;
	}
	/* this driver does not support primary backup */
	if (val->mxSpanPrimary != 0)
		goto einval;
	return (0);
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_test_config_chan(struct sp *sp, struct ch *ch, mx_config_t * arg)
{
	struct cd *cd;
	struct mx_conf_chan *val = (typeof(val)) (arg + 1);
	uint chan;

	cd = sp->cd;
	chan = (arg->id >> 0) & 0xff;
	if (val->mxCardIndex != cd->card)
		goto einval;
	if (val->mxSpanIndex != sp->span)
		goto einval;
	switch (sp->config.ifgtype) {
	default:
	case SDL_GTYPE_E1:
		if (1 > chan || chan > 31)
			goto einval;
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		if (1 > chan || chan > 24)
			goto einval;
		break;
	}
	return (0);
      einval:
	return (-EINVAL);
}

static noinline __unlikely int
mx_commit_config_dflt(mx_config_t * arg)
{
	/* not configuration to commit */
	return (0);
}

static noinline __unlikely int
mx_commit_config_sync(struct sg *sg, mx_config_t * arg)
{
	return (0);
}

static noinline __unlikely int
mx_commit_config_card(struct cd *cd, mx_config_t * arg)
{
	struct sp *sp;
	struct ch *ch;
	struct mx_conf_card *val = (typeof(val)) (arg + 1);
	uint span, chan;
	uint card_reconfig = 0, span_reconfig = 0, chan_reconfig = 0;

	switch (val->mxCardSpanType) {
	case MXCARDSPANTYPE_NONE:
		break;
	case MXCARDSPANTYPE_E1:
		if (cd->config.ifgtype == SDL_GTYPE_E1)
			break;
		card_reconfig |= (1 << cd->card);
		cd->config.ifgtype = SDL_GTYPE_E1;
		for (span = 0; span < cd->ports; span++) {
			if ((sp = cd->spans[span]) == NULL)
				continue;
			if (sp->config.ifgtype == SDL_GTYPE_E1)
				continue;
			span_reconfig |= (1 << sp->span);
			sp->config.ifgtype = SDL_GTYPE_E1;
			sp->config.ifgcrc = SDL_GCRC_CRC4;
			sp->config.ifcoding = SDL_CODING_HDB3;
			sp->config.ifframing = SDL_FRAMING_CCS;
			for (chan = 1; chan < 32; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				chan_reconfig |= (1 << (chan - 1));
				ch->sdl.config.ifgtype = SDL_GTYPE_E1;
				ch->sdl.config.ifgcrc = SDL_GCRC_CRC4;
				ch->sdl.config.ifcoding = SDL_CODING_HDB3;
				ch->sdl.config.ifframing = SDL_FRAMING_CCS;
				switch (ch->sdl.config.iftype) {
				case SDL_TYPE_T1:
				case SDL_TYPE_J1:
					ch->sdl.config.iftype = SDL_TYPE_E1;
				}
			}
		}
		break;
	case MXCARDSPANTYPE_T1:
		if (cd->config.ifgtype == SDL_GTYPE_T1)
			break;
		card_reconfig |= (1 << cd->card);
		cd->config.ifgtype = SDL_GTYPE_T1;
		for (span = 0; span < cd->ports; span++) {
			if ((sp = cd->spans[span]) == NULL)
				continue;
			if (sp->config.ifgtype == SDL_GTYPE_T1)
				continue;
			span_reconfig |= (1 << sp->span);
			switch (sp->config.ifgtype) {
			case SDL_GTYPE_J1:
				sp->config.ifgtype = SDL_GTYPE_T1;
				sp->config.ifgcrc = SDL_GCRC_CRC6;
				for (chan = 1; chan <= 24; chan++) {
					if (!(ch = sp->chans[chan]))
						continue;
					chan_reconfig |= (1 << (chan - 1));
					ch->sdl.config.ifgtype = SDL_GTYPE_T1;
					ch->sdl.config.ifgcrc = SDL_GCRC_CRC6;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_J1:
						ch->sdl.config.iftype = SDL_TYPE_T1;
					}
				}
				break;
			case SDL_GTYPE_E1:
				sp->config.ifgtype = SDL_GTYPE_T1;
				sp->config.ifgcrc = SDL_GCRC_CRC6;
				sp->config.ifcoding = SDL_CODING_B8ZS;
				sp->config.ifframing = SDL_FRAMING_ESF;
				for (chan = 1; chan <= 24; chan++) {
					if ((ch = sp->chans[chan]) == NULL)
						continue;
					chan_reconfig |= (1 << (chan - 1));
					ch->sdl.config.ifgtype = SDL_GTYPE_T1;
					ch->sdl.config.ifgcrc = SDL_GCRC_CRC6;
					ch->sdl.config.ifcoding = SDL_CODING_B8ZS;
					ch->sdl.config.ifframing = SDL_FRAMING_ESF;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_E1:
						ch->sdl.config.iftype = SDL_TYPE_T1;
					}
				}
				break;
			}
		}
		break;
	case MXCARDSPANTYPE_J1:
		if (cd->config.ifgtype == SDL_GTYPE_J1)
			break;
		card_reconfig |= (1 << cd->card);
		cd->config.ifgtype = SDL_GTYPE_J1;
		for (span = 0; span < cd->ports; span++) {
			if ((sp = cd->spans[span]) == NULL)
				continue;
			if (sp->config.ifgtype == SDL_GTYPE_J1)
				continue;
			span_reconfig |= (1 << sp->span);
			switch (sp->config.ifgtype) {
			case SDL_GTYPE_T1:
				sp->config.ifgtype = SDL_GTYPE_J1;
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				for (chan = 1; chan <= 24; chan++) {
					if ((ch = sp->chans[chan]) == NULL)
						continue;
					chan_reconfig |= (1 << (chan - 1));
					ch->sdl.config.ifgtype = SDL_GTYPE_J1;
					ch->sdl.config.ifgcrc = SDL_GCRC_CRC6J;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_T1:
						ch->sdl.config.iftype = SDL_TYPE_J1;
					}
				}
				break;
			case SDL_GTYPE_E1:
				sp->config.ifgtype = SDL_GTYPE_J1;
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				sp->config.ifcoding = SDL_CODING_B8ZS;
				sp->config.ifframing = SDL_FRAMING_ESF;
				for (chan = 1; chan <= 24; chan++) {
					if ((ch = sp->chans[chan]) == NULL)
						continue;
					chan_reconfig |= (1 << (chan - 1));
					ch->sdl.config.ifgtype = SDL_GTYPE_J1;
					ch->sdl.config.ifgcrc = SDL_GCRC_CRC6J;
					ch->sdl.config.ifcoding = SDL_CODING_B8ZS;
					ch->sdl.config.ifframing = SDL_FRAMING_ESF;
					switch (ch->sdl.config.iftype) {
					case SDL_TYPE_E1:
						ch->sdl.config.iftype = SDL_TYPE_J1;
					}
				}
				break;
			}
		}
		break;
	}
	if (!card_reconfig || !(cd->config.ifflags & SDL_IF_UP))
		return (0);
	xp_card_reconfig(cd, 0);
	for (span = 0; span < cd->ports; span++) {
		if ((sp = cd->spans[span]) == NULL)
			continue;
		if (!(span_reconfig & (1 << span))
		    || !(sp->config.ifflags & SDL_IF_UP))
			continue;
		xp_span_reconfig(sp);
		switch (sp->config.ifgtype) {
		case SDL_GTYPE_E1:
			for (chan = 1; chan <= 31; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				if ((chan_reconfig & (1 << (chan - 1)))
				    && (ch->sdl.config.ifflags & SDL_IF_UP)) {
					/* xp_chan_reconfig(ch); */
				}
			}
			break;
		case SDL_GTYPE_T1:
		case SDL_GTYPE_J1:
			for (chan = 1; chan <= 24; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				if ((chan_reconfig & (1 << (chan - 1)))
				    && (ch->sdl.config.ifflags & SDL_IF_UP)) {
					/* xp_chan_reconfig(ch); */
				}
			}
			break;
		}
	}
	return (0);
}

static noinline __unlikely int
mx_commit_config_span(struct sp *sp, mx_config_t * arg)
{
	return (0);
}

static noinline __unlikely int
mx_commit_config_chan(struct ch *ch, mx_config_t * arg)
{
	return (0);
}

noinline __unlikely int
mx_iocsconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_config_t *arg = (typeof(arg)) dp->b_rptr;
	int ret;

	switch (arg->cmd) {
	case MX_CHA:
		break;
	case MX_ADD:
	case MX_DEL:
		/* cannot insert or delete entries */
		return (-EOPNOTSUPP);
	default:
		return (-EINVAL);
	}

	ret = -ESRCH;

	switch (arg->type) {
		struct sg *sg;
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		if ((ret = mx_test_config_dflt(arg)) >= 0)
			ret = mx_commit_config_dflt(arg);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* syncrhonization groups not supported by driver */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		if ((ret = mx_test_config_sync(sg, arg)) >= 0)
			ret = mx_commit_config_sync(sg, arg);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		if ((ret = mx_test_config_card(cd, arg)) >= 0)
			ret = mx_commit_config_card(cd, arg);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		if ((ret = mx_test_config_span(sp, arg)) >= 0)
			ret = mx_commit_config_span(sp, arg);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		if ((ret = mx_test_config_chan(sp, ch, arg)) >= 0)
			ret = mx_commit_config_chan(ch, arg);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* fractionals not supported by driver */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* cross-connect not supported by driver */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* bert not supported by driver */
		break;
	}
	return (ret);
}

noinline __unlikely int
mx_ioctconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_config_t *arg = (typeof(arg)) dp->b_rptr;
	int ret;

	arg->cmd = MX_TST;
	ret = -ESRCH;

	switch (arg->type) {
		struct sg *sg;
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_test_config_dflt(arg);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* syncrhonization groups not supported by driver */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_test_config_sync(sg, arg);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_test_config_card(cd, arg);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_test_config_span(sp, arg);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_test_config_chan(sp, ch, arg);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* fractionals not supported by driver */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* cross-connect not supported by driver */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* bert not supported by driver */
		break;
	}
	return (ret);
}

noinline __unlikely int
mx_ioccconfig(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_config_t *arg = (typeof(arg)) dp->b_rptr;
	int ret;

	arg->cmd = MX_COM;
	ret = -ESRCH;

	switch (arg->type) {
		struct sg *sg;
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_commit_config_dflt(arg);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* syncrhonization groups not supported by driver */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_commit_config_sync(sg, arg);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_commit_config_card(cd, arg);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_commit_config_span(sp, arg);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_commit_config_chan(ch, arg);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* fractionals not supported by driver */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* cross-connect not supported by driver */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* bert not supported by driver */
		break;
	}
	return (ret);
}

STATIC __unlikely int
mx_statem_size(mx_statem_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->statem->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->statem->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->statem->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->statem->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->statem->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->statem->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->statem->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->statem->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgstatem_dflt(mx_statem_t * arg)
{
	/* no state information for default */
	return (0);
}

noinline __unlikely int
mx_iocgstatem_sync(struct sg *sg, mx_statem_t * arg)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
mx_iocgstatem_card(struct cd *cd, mx_statem_t * arg)
{
	/* no state information for cards */
	return (0);
}

noinline __unlikely int
mx_iocgstatem_span(struct sp *sp, mx_statem_t * arg)
{
	/* no state information for spans */
	return (0);
}

noinline __unlikely int
mx_iocgstatem_chan(struct sp *sp, struct ch *ch, mx_statem_t * arg)
{
	uint chan;

	chan = arg->id & 0xff;
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		if (1 > chan || chan > 31)
			goto esrch;
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		if (1 > chan || chan > 24)
			goto esrch;
		break;
	default:
		goto esrch;
	}
	/* no state information for channels */
	return (0);
      esrch:
	return (-ESRCH);
}

noinline __unlikely int
mx_iocgstatem(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_statem_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = mx_statem_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgstatem_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgstatem_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgstatem_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgstatem_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgstatem_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (ret);
}

noinline __unlikely int
mx_ioccmreset(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_statem_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
	case MX_OBJ_TYPE_CARD:	/* card */
	case MX_OBJ_TYPE_SPAN:	/* span */
	case MX_OBJ_TYPE_CHAN:	/* channel */
	case MX_OBJ_TYPE_FRAC:	/* fractional */
	case MX_OBJ_TYPE_XCON:	/* cross connect */
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		return (0);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

STATIC __unlikely int
mx_status_size(mx_status_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->status->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->status->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->status->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->status->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->status->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->status->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->status->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->status->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgstatus_dflt(mx_status_t * arg)
{
	/* there are no defined status fields for the default object */
	return (0);
}

noinline __unlikely int
mx_iocgstatus_sync(struct sg *sg, mx_status_t * arg)
{
	/* there are no defined status fields for the synchronization object */
	return (0);
}

/** mx_iocgstatus_card: - get the card MX status
  * @cd: card structure pointer
  * @arg: place to put status
  */
noinline __unlikely int
mx_iocgstatus_card(struct cd *cd, mx_status_t * arg)
{
	struct mx_status_card *val = (typeof(val)) (arg + 1);

	/* mxCardAdministrativeState */
	if (cd->config.ifflags & SDL_IF_UP)
		val->mxCardAdministrativeState = X721_ADMINISTRATIVESTATE_UNLOCKED;
	else
		val->mxCardAdministrativeState = X721_ADMINISTRATIVESTATE_LOCKED;
	/* mxCardOperationalState */
	/* mxCardUsageState */
	if (!(cd->config.ifflags & SDL_IF_UP)) {
		val->mxCardOperationalState = X721_OPERATIONALSTATE_DISABLED;
		val->mxCardUsageState = X721_USAGESTATE_IDLE;
		val->mxCardAvailabilityStatus = 0;
		val->mxCardAlarmStatus = 0;
	} else {
		uint span, used = 0, busy = 1, depend = 0, alarms = 0;

		val->mxCardOperationalState = X721_OPERATIONALSTATE_ENABLED;
		for (span = 0; span < cd->ports; span++) {
			struct sp *sp;

			if ((sp = cd->spans[span]) && (sp->config.ifflags & SDL_IF_UP)) {
				uint chan;
				struct ch *ch;

				if (sp->config.ifgtype == SDL_GTYPE_E1) {
					for (chan = 1; chan <= 31; chan++)
						if ((ch = sp->chans[chan]))
							used = 1;
						else
							busy = 0;
				} else {
					for (chan = 1; chan <= 24; chan++)
						if ((ch = sp->chans[chan]))
							used = 1;
						else
							busy = 0;
				}
				alarms |= sp->config.ifalarms;
			} else {
				depend = 1;
			}
		}
		if (busy)
			val->mxCardUsageState = X721_USAGESTATE_BUSY;
		else if (used)
			val->mxCardUsageState = X721_USAGESTATE_ACTIVE;
		else
			val->mxCardUsageState = X721_USAGESTATE_IDLE;
		if (depend)
			val->mxCardAvailabilityStatus = (1 << X721_AVAILABILITYSTATUS_DEPENDENCY);
		else
			val->mxCardAvailabilityStatus = 0;
		val->mxCardAlarmStatus = 0;
		if (alarms & SDL_ALARM_YEL) {
			val->mxCardAlarmStatus |= (1 << X721_ALARMSTATUS_MINOR);
			val->mxCardAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEGRADED);
		}
		if (alarms & SDL_ALARM_RED) {
			val->mxCardAlarmStatus |= (1 << X721_ALARMSTATUS_MAJOR);
			val->mxCardAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_FAILED);
		}
		if (alarms & SDL_ALARM_BLU) {
			val->mxCardAlarmStatus |= (1 << X721_ALARMSTATUS_ALARMOUTSTANDING);
			val->mxCardAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEPENDENCY);
		}
	}
	/* mxCardAlarmStatus */
	/* mxCardProceduralStatus */
	val->mxCardProceduralStatus = 0;
	if (!(cd->config.ifflags & SDL_IF_UP))
		val->mxCardProceduralStatus |= (1 << X721_PROCEDURALSTATUS_INITIALIZATIONREQUIRED);
	if (!(cd->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)))
		val->mxCardProceduralStatus |= (1 << X721_PROCEDURALSTATUS_NOTINITIALIZED);
	/* mxCardAvailabilityStatus */
	/* mxCardControlStatus */
	val->mxCardControlStatus = 0;
	if (!(cd->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))
	    && (val->mxCardUsageState != X721_USAGESTATE_IDLE))
		val->mxCardControlStatus |= (1 << X721_CONTROLSTATUS_SUSPENDED);
	/* mxCardUnknownStatus */
	val->mxCardUnknownStatus = X721_UNKNOWNSTATUS_FALSE;
	/* mxCardStandbyStatus */
	val->mxCardStandbyStatus = X721_STANDBYSTATUS_PROVIDINGSERVICE;
	return (0);
}

/** mx_iocgstatus_span: - get the status of a span
  * @sp: span structure pointer
  * @arg: place to put status
  */
noinline __unlikely int
mx_iocgstatus_span(struct sp *sp, mx_status_t * arg)
{
	struct mx_status_span *val = (typeof(val)) (arg + 1);

	/* mxSpanAdministrativeState */
	if (sp->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))
		val->mxSpanAdministrativeState = X721_ADMINISTRATIVESTATE_UNLOCKED;
	else
		val->mxSpanAdministrativeState = X721_ADMINISTRATIVESTATE_LOCKED;
	/* mxSpanOperationalState */
	if (!(sp->config.ifflags & SDL_IF_UP)) {
		val->mxSpanOperationalState = X721_OPERATIONALSTATE_DISABLED;
		val->mxSpanUsageState = X721_USAGESTATE_IDLE;
		val->mxSpanAvailabilityStatus = 0;
		val->mxSpanAlarmStatus = 0;
		val->mxSpanAlarms = 0;
	} else {
		uint used = 0, busy = 1, chan;
		struct ch *ch;

		val->mxSpanOperationalState = X721_OPERATIONALSTATE_ENABLED;

		if (sp->config.ifgtype == SDL_GTYPE_E1) {
			for (chan = 1; chan <= 31; chan++)
				if ((ch = sp->chans[chan]))
					used = 1;
				else
					busy = 0;
		} else {
			for (chan = 1; chan <= 24; chan++)
				if ((ch = sp->chans[chan]))
					used = 1;
				else
					busy = 0;
		}
		if (busy)
			val->mxSpanUsageState = X721_USAGESTATE_BUSY;
		else if (used)
			val->mxSpanUsageState = X721_USAGESTATE_ACTIVE;
		else
			val->mxSpanUsageState = X721_USAGESTATE_IDLE;
		if (!(sp->cd->config.ifflags & SDL_IF_UP)
		    || !(sp->cd->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)))
			val->mxSpanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEPENDENCY);
		else
			val->mxSpanAvailabilityStatus = 0;
		/* mxSpanAlarms */
		val->mxSpanAlarms = 0;
		if (sp->config.ifalarms & SDL_ALARM_YEL) {
			val->mxSpanAlarms |= (1 << MXSPANALARMS_YELLOW);
			val->mxSpanAlarmStatus |= (1 << X721_ALARMSTATUS_MINOR);
			val->mxSpanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEGRADED);
		}
		if (sp->config.ifalarms & SDL_ALARM_RED) {
			val->mxSpanAlarms |= (1 << MXSPANALARMS_RED);
			val->mxSpanAlarmStatus |= (1 << X721_ALARMSTATUS_MAJOR);
			val->mxSpanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_FAILED);
		}
		if (sp->config.ifalarms & SDL_ALARM_BLU) {
			val->mxSpanAlarms |= (1 << MXSPANALARMS_BLUE);
			val->mxSpanAlarmStatus |= (1 << X721_ALARMSTATUS_ALARMOUTSTANDING);
			val->mxSpanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEPENDENCY);
		}
		if (sp->config.ifalarms & SDL_ALARM_REC)
			val->mxSpanAlarms |= (1 << MXSPANALARMS_RECOVERY);
		if (sp->config.ifalarms & SDL_ALARM_DMF)
			val->mxSpanAlarms |= (1 << MXSPANALARMS_DISTMF);
	}
	/* mxSpanUsageState */
	/* mxSpanAlarmStatus */
	/* mxSpanProceduralStatus */
	val->mxSpanProceduralStatus = 0;
	if (!(sp->config.ifflags & SDL_IF_UP))
		val->mxSpanProceduralStatus |= (1 << X721_PROCEDURALSTATUS_INITIALIZATIONREQUIRED);
	if (!(sp->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)))
		val->mxSpanProceduralStatus |= (1 << X721_PROCEDURALSTATUS_NOTINITIALIZED);
	/* mxSpanAvailabilityStatus */
	/* mxSpanControlStatus */
	val->mxSpanControlStatus = 0;
	if (!(sp->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))
	    && (val->mxSpanUsageState != X721_USAGESTATE_IDLE))
		val->mxSpanControlStatus |= (1 << X721_CONTROLSTATUS_SUSPENDED);
	/* mxSpanStandbyStatus */
	val->mxSpanStandbyStatus = X721_STANDBYSTATUS_PROVIDINGSERVICE;
	/* mxSpanUnknownStatus */
	val->mxSpanUnknownStatus = X721_UNKNOWNSTATUS_FALSE;
	/* mxSpanLoopbackStatus */
	val->mxSpanLoopbackStatus = 0;	/* for now */
	/* mxSpanLineStatus */
	val->mxSpanLineStatus = (1 << MXSPANLINESTATUS_NONE);	/* for now */
	/* mxSpanEvents */
	val->mxSpanEvents = 0;
	if (sp->status.flags & (XP_STATE_RLOS))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_RLOS);
	if (sp->status.flags & (XP_STATE_FRCL))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_FRCL);
	if (sp->status.flags & (XP_STATE_RUA1))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_RUAL);
	if (sp->status.flags & (XP_STATE_RYEL))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_RYEL);
	if (sp->status.flags & (XP_STATE_RRA))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_RRA);
	if (sp->status.flags & (XP_STATE_RDMA))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_RDMA);
#if 0
	if (sp->status.flags & (XP_STATE_V52LNK))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_V52LNK);
#endif
	if (sp->status.flags & (XP_STATE_LORC))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_LORC);
	if (sp->status.flags & (XP_STATE_LOTC))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_LOTC);
	if (sp->status.flags & (XP_STATE_LUP))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_LUP);
	if (sp->status.flags & (XP_STATE_LDN))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_LDN);
	if (sp->status.flags & (XP_STATE_SPARE))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_LSPARE);
	if (sp->status.flags & (XP_STATE_TOCD))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_TOCD);
	if (sp->status.flags & (XP_STATE_TSCD))
		val->mxSpanEvents |= (1 << MXSPANEVENTS_TCLE);
	return (0);
}

noinline __unlikely int
mx_iocgstatus_chan(struct sp *sp, struct ch *ch, mx_status_t * arg)
{
	struct mx_status_chan *val = (typeof(val)) (arg + 1);

	/* mxChanAdministrativeState */
	if (!ch || ch->sdl.config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))
		val->mxChanAdministrativeState = X721_ADMINISTRATIVESTATE_UNLOCKED;
	else
		val->mxChanAdministrativeState = X721_ADMINISTRATIVESTATE_LOCKED;
	val->mxChanAlarmStatus = 0;
	/* mxChanOperationalState */
	if (!ch || !(ch->sdl.config.ifflags & SDL_IF_UP)) {
		val->mxChanOperationalState = X721_OPERATIONALSTATE_DISABLED;
		val->mxChanUsageState = X721_USAGESTATE_IDLE;
		val->mxChanAvailabilityStatus = 0;
	} else {
		val->mxChanOperationalState = X721_OPERATIONALSTATE_ENABLED;
		val->mxChanUsageState = X721_USAGESTATE_BUSY;
		if (!ch || !(ch->sp->config.ifflags & SDL_IF_UP)
		    || !(ch->sp->config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)))
			val->mxChanAvailabilityStatus = (1 << X721_AVAILABILITYSTATUS_DEPENDENCY);
		else
			val->mxChanAvailabilityStatus = 0;
		if (ch) {
			if (ch->sp->config.ifalarms & SDL_ALARM_YEL) {
				val->mxChanAlarmStatus |= (1 << X721_ALARMSTATUS_MINOR);
				val->mxChanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEGRADED);
			}
			if (ch->sp->config.ifalarms & SDL_ALARM_RED) {
				val->mxChanAlarmStatus |= (1 << X721_ALARMSTATUS_MAJOR);
				val->mxChanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_FAILED);
			}
			if (ch->sp->config.ifalarms & SDL_ALARM_BLU) {
				val->mxChanAlarmStatus |= (1 << X721_ALARMSTATUS_ALARMOUTSTANDING);
				val->mxChanAvailabilityStatus |= (1 << X721_AVAILABILITYSTATUS_DEPENDENCY);
			}
		}
	}
	/* mxChanUsageState */
	/* mxChanAvailabilityStatus */
	/* mxChanControlStatus */
	val->mxChanControlStatus = 0;
	if (!(ch->sdl.config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING))
	    && (val->mxChanUsageState != X721_USAGESTATE_IDLE))
		val->mxChanControlStatus |= (1 << X721_CONTROLSTATUS_SUSPENDED);
	/* mxChanProceduralStatus */
	val->mxChanProceduralStatus = 0;
	if (!(ch->sdl.config.ifflags & SDL_IF_UP))
		val->mxChanProceduralStatus |= (1 << X721_PROCEDURALSTATUS_INITIALIZATIONREQUIRED);
	if (!(ch->sdl.config.ifflags & (SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)))
		val->mxChanProceduralStatus |= (1 << X721_PROCEDURALSTATUS_NOTINITIALIZED);
	/* mxChanAlarmStatus */
	/* mxChanStandbyStatus */
	val->mxChanStandbyStatus = X721_STANDBYSTATUS_PROVIDINGSERVICE;
	return (0);
}

noinline __unlikely int
mx_iocgstatus(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_status_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = mx_status_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgstatus_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgstatus_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgstatus_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgstatus_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgstatus_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	mi_copyout(q, mp);
	return (0);
}

noinline __unlikely int
mx_iocsstatus(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_status_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		return (0);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		return (0);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		return (0);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		return (0);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_ioccstatus(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_status_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		return (0);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		return (0);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		return (0);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		return (0);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

STATIC __unlikely int
mx_stats_size(mx_stats_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->stats->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->stats->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->stats->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->stats->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->stats->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->stats->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->stats->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->stats->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgstatsp(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_stats_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		return (0);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		return (0);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		return (0);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		return (0);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		return (0);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_iocsstatsp(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_stats_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		return (0);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		return (0);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		return (0);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		return (0);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		return (0);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_iocgstats_dflt(mx_stats_t * arg)
{
	struct mx_stats_dflt *val = (typeof(val)) (arg + 1);

	/* FIXME: get these */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
mx_iocgstats_sync(struct sg *sg, mx_stats_t * arg)
{
	struct mx_stats_sync *val = (typeof(val)) (arg + 1);

	/* FIXME: get these */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
mx_iocgstats_card(struct cd *cd, mx_stats_t * arg)
{
	struct mx_stats_card *val = (typeof(val)) (arg + 1);

	/* FIXME: get these */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
mx_iocgstats_span(struct sp *sp, mx_stats_t * arg)
{
	struct mx_stats_span *val = (typeof(val)) (arg + 1);
	uint interval, valid, invalid;
	struct st *st, total;

	/* FIXME: get these */
	bzero(val, sizeof(*val));
	for (valid = 0, invalid = 0, interval = 0; interval < 96; interval++) {
		st = &sp->hist[interval];

		if (st->ValidData)
			valid++;
		else
			invalid++;
	}
	val->mxValidIntervals = valid;
	val->mxInvalidIntervals = invalid;
	
	if (arg->interval == 0)
		st = &sp->stats[2];
	else if (arg->interval > 96 && arg->interval != 0xffffffff)
		return (-EINVAL);
	else if (sp->hist == NULL)
		return (-EINVAL);
	else if (arg->interval == 0xffffffff) {
		bzero(&total, sizeof(total));
		for (interval = 0; interval < 96; interval++) {
			st = &sp->hist[interval];

			if (st->ValidData) {
				total.ESs += st->ESs;
				total.SESs += st->SESs;
				total.UASs += st->UASs;
				total.CSSs += st->CSSs;
				total.PCVs += st->PCVs;
				total.LESs += st->LESs;
				total.BESs += st->BESs;
				total.DMs += st->DMs;
				total.LCVs += st->LCVs;
				total.FASEs += st->FASEs;
				total.FABEs += st->FABEs;
				total.FEBEs += st->FEBEs;
			}
		}
		if (valid)
			total.ValidData = 2; /* true(2) */
		else
			total.ValidData = 1; /* false(1) */
		st = &total;
	} else
		st = &sp->hist[(sp->curr + (arg->interval - 1)) % 96];
	val->mxNearEndESs = st->ESs;
	val->mxNearEndSESs = st->SESs;
	val->mxNearEndUASs = st->UASs;
	val->mxNearEndCSSs = st->CSSs;
	val->mxNearEndPCVs = st->PCVs;
	val->mxNearEndLESs = st->LESs;
	val->mxNearEndBESs = st->BESs;
	val->mxNearEndDMs = st->DMs;
	val->mxNearEndLCVs = st->LCVs;
	val->mxNearEndFASEs = st->FASEs;
	val->mxNearEndFABEs = st->FABEs;
	val->mxNearEndFEBEs = st->FEBEs;
	val->mxNearEndValidData = st->ValidData;
	/* FIXME: don't support far-end collection yet... */
	val->mxFarEndESs = 0;
	val->mxFarEndSESs = 0;
	val->mxFarEndUASs = 0;
	val->mxFarEndCSSs = 0;
	val->mxFarEndPCVs = 0;
	val->mxFarEndLESs = 0;
	val->mxFarEndBESs = 0;
	val->mxFarEndDMs = 0;
	val->mxFarEndValidData = 1; /* false(1) */
	return (0);
}

noinline __unlikely int
mx_iocgstats_chan(struct sp *sp, struct ch *ch, mx_stats_t * arg)
{
	struct mx_stats_chan *val = (typeof(val)) (arg + 1);

	/* FIXME: get these */
	bzero(val, sizeof(*val));
	return (0);
}

noinline __unlikely int
mx_iocgstats(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_stats_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = mx_stats_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgstats_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgstats_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgstats_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgstats_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgstats_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (ret);
}

noinline __unlikely int
mx_ioccstats(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_stats_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		return (0);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		return (0);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		return (0);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		return (0);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		return (0);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

STATIC __unlikely int
mx_notify_size(mx_notify_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->events->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->events->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->events->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->events->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->events->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->events->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->events->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->events->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgnotify_dflt(mx_notify_t * arg)
{
	/* FIXME: complete this */
	return (0);
}

noinline __unlikely int
mx_iocgnotify_sync(struct sg *sg, mx_notify_t * arg)
{
	/* FIXME: complete this */
	return (-EOPNOTSUPP);
}

noinline __unlikely int
mx_iocgnotify_card(struct cd *cd, mx_notify_t * arg)
{
	/* FIXME: complete this */
	return (0);
}

noinline __unlikely int
mx_iocgnotify_span(struct sp *sp, mx_notify_t * arg)
{
	/* FIXME: complete this */
	return (0);
}

noinline __unlikely int
mx_iocgnotify_chan(struct sp *sp, struct ch *ch, mx_notify_t * arg)
{
	/* FIXME: complete this */
	return (0);
}

noinline __unlikely int
mx_iocgnotify(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_notify_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = mx_notify_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgnotify_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgnotify_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgnotify_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgnotify_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgnotify_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (ret);
}

noinline __unlikely int
mx_iocsnotify(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_notify_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

noinline __unlikely int
mx_ioccnotify(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_notify_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

STATIC __unlikely int
mx_attr_size(mx_attr_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->attrs->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->attrs->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->attrs->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->attrs->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->attrs->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->attrs->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->attrs->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->attrs->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_iocgattr_dflt(mx_attr_t * arg)
{
	int ret;

	if ((ret = mx_iocgconfig_dflt((mx_config_t *) & arg->attrs->dflt.config)) < 0)
		goto error;
	if ((ret = mx_iocgoption_dflt((mx_option_t *) & arg->attrs->dflt.option)) < 0)
		goto error;
	if ((ret = mx_iocginfo_dflt((mx_info_t *) & arg->attrs->dflt.inform)) < 0)
		goto error;
	if ((ret = mx_iocgstatem_dflt((mx_statem_t *) & arg->attrs->dflt.statem)) < 0)
		goto error;
	if ((ret = mx_iocgstatus_dflt((mx_status_t *) & arg->attrs->dflt.status)) < 0)
		goto error;
	if ((ret = mx_iocgstats_dflt((mx_stats_t *) & arg->attrs->dflt.stats)) < 0)
		goto error;
	if ((ret = mx_iocgnotify_dflt((mx_notify_t *) & arg->attrs->dflt.events)) < 0)
		goto error;
	return (0);
      error:
	return (ret);
}

noinline __unlikely int
mx_iocgattr_sync(struct sg *sg, mx_attr_t * arg)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
mx_iocgattr_card(struct cd *cd, mx_attr_t * arg)
{
	int ret;

	if ((ret = mx_iocgconfig_card(cd, (mx_config_t *) & arg->attrs->card.config)) < 0)
		goto error;
	if ((ret = mx_iocgoption_card(cd, (mx_option_t *) & arg->attrs->card.option)) < 0)
		goto error;
	if ((ret = mx_iocginfo_card(cd, (mx_info_t *) & arg->attrs->card.inform)) < 0)
		goto error;
	if ((ret = mx_iocgstatem_card(cd, (mx_statem_t *) & arg->attrs->card.statem)) < 0)
		goto error;
	if ((ret = mx_iocgstatus_card(cd, (mx_status_t *) & arg->attrs->card.status)) < 0)
		goto error;
	if ((ret = mx_iocgstats_card(cd, (mx_stats_t *) & arg->attrs->card.stats)) < 0)
		goto error;
	if ((ret = mx_iocgnotify_card(cd, (mx_notify_t *) & arg->attrs->card.events)) < 0)
		goto error;
	return (0);
      error:
	return (ret);
}

noinline __unlikely int
mx_iocgattr_span(struct sp *sp, mx_attr_t * arg)
{
	int ret;

	if ((ret = mx_iocgconfig_span(sp, (mx_config_t *) & arg->attrs->span.config)) < 0)
		goto error;
	if ((ret = mx_iocgoption_span(sp, (mx_option_t *) & arg->attrs->span.option)) < 0)
		goto error;
	if ((ret = mx_iocginfo_span(sp, (mx_info_t *) & arg->attrs->span.inform)) < 0)
		goto error;
	if ((ret = mx_iocgstatem_span(sp, (mx_statem_t *) & arg->attrs->span.statem)) < 0)
		goto error;
	if ((ret = mx_iocgstatus_span(sp, (mx_status_t *) & arg->attrs->span.status)) < 0)
		goto error;
	if ((ret = mx_iocgstats_span(sp, (mx_stats_t *) & arg->attrs->span.stats)) < 0)
		goto error;
	if ((ret = mx_iocgnotify_span(sp, (mx_notify_t *) & arg->attrs->span.events)) < 0)
		goto error;
	return (0);
      error:
	return (ret);
}

noinline __unlikely int
mx_iocgattr_chan(struct sp *sp, struct ch *ch, mx_attr_t * arg)
{
	int ret;

	if ((ret = mx_iocgconfig_chan(sp, ch, (mx_config_t *) & arg->attrs->chan.config)) < 0)
		goto error;
	if ((ret = mx_iocgoption_chan(sp, ch, (mx_option_t *) & arg->attrs->chan.option)) < 0)
		goto error;
	if ((ret = mx_iocginfo_chan(sp, ch, (mx_info_t *) & arg->attrs->chan.inform)) < 0)
		goto error;
	if ((ret = mx_iocgstatem_chan(sp, ch, (mx_statem_t *) & arg->attrs->chan.statem)) < 0)
		goto error;
	if ((ret = mx_iocgstatus_chan(sp, ch, (mx_status_t *) & arg->attrs->chan.status)) < 0)
		goto error;
	if ((ret = mx_iocgstats_chan(sp, ch, (mx_stats_t *) & arg->attrs->chan.stats)) < 0)
		goto error;
	if ((ret = mx_iocgnotify_chan(sp, ch, (mx_notify_t *) & arg->attrs->chan.events)) < 0)
		goto error;
	return (0);
      error:
	return (ret);
}

noinline __unlikely int
mx_iocgattr(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_attr_t *hdr, *arg = (typeof(arg)) dp->b_rptr;
	int ret;
	mblk_t *db;

	if ((ret = mx_attr_size(arg)) < 0)
		return (ret);
	if (!(db = mi_copyout_alloc(q, mp, 0, ret, 0)))
		return (-ENOSR);

	*(hdr = (typeof(hdr)) db->b_rptr) = *arg;

	ret = -ESRCH;

	switch (arg->type) {
		struct cd *cd;
		struct sp *sp;
		struct ch *ch;
		struct sg *sg;
		psw_t flags;

	case MX_OBJ_TYPE_DFLT:	/* defaults */
		if (arg->id != 0)
			break;
		ret = mx_iocgattr_dflt(hdr);
		break;
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		if (!(sg = sg_find(arg->id)))
			break;
		spin_lock_irqsave(&sg->lock, flags);
		ret = mx_iocgattr_sync(sg, hdr);
		spin_unlock_irqrestore(&sg->lock, flags);
		break;
	case MX_OBJ_TYPE_CARD:	/* card */
		if (!(cd = cd_find(arg->id)))
			break;
		spin_lock_irqsave(&cd->lock, flags);
		ret = mx_iocgattr_card(cd, hdr);
		spin_unlock_irqrestore(&cd->lock, flags);
		break;
	case MX_OBJ_TYPE_SPAN:	/* span */
		if (!(sp = sp_find(arg->id)))
			break;
		spin_lock_irqsave(&sp->lock, flags);
		ret = mx_iocgattr_span(sp, hdr);
		spin_unlock_irqrestore(&sp->lock, flags);
		break;
	case MX_OBJ_TYPE_CHAN:	/* channel */
		if (!(sp = sp_find(arg->id)))
			break;
		if (IS_ERR((ch = ch_find(arg->id))))
			break;
		spin_lock_bh(&ch->lock);
		ret = mx_iocgattr_chan(sp, ch, hdr);
		spin_unlock_bh(&ch->lock);
		break;
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		break;
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		break;
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		break;
	default:
		ret = -EINVAL;
		break;
	}
	if (ret >= 0)
		mi_copyout(q, mp);
	return (0);
}

STATIC __unlikely int
mx_mgmt_size(mx_mgmt_t * arg)
{
	int size = sizeof(*arg);

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:
		size += sizeof(arg->action->dflt);
		break;
	case MX_OBJ_TYPE_SYNC:
		size += sizeof(arg->action->sync);
		break;
	case MX_OBJ_TYPE_CARD:
		size += sizeof(arg->action->card);
		break;
	case MX_OBJ_TYPE_SPAN:
		size += sizeof(arg->action->span);
		break;
	case MX_OBJ_TYPE_CHAN:
		size += sizeof(arg->action->chan);
		break;
	case MX_OBJ_TYPE_FRAC:
		size += sizeof(arg->action->frac);
		break;
	case MX_OBJ_TYPE_XCON:
		size += sizeof(arg->action->xcon);
		break;
	case MX_OBJ_TYPE_BERT:
		size += sizeof(arg->action->bert);
		break;
	default:
		return (-EINVAL);
	}
	return (size);
}

noinline __unlikely int
mx_ioccmgmt(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	mx_mgmt_t *arg = (typeof(arg)) dp->b_rptr;

	switch (arg->type) {
	case MX_OBJ_TYPE_DFLT:	/* defaults */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SYNC:	/* synchronization group */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CARD:	/* card */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_SPAN:	/* span */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_CHAN:	/* channel */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_FRAC:	/* fractional */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_XCON:	/* cross connect */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	case MX_OBJ_TYPE_BERT:	/* bit error rate test */
		/* FIXME: complete this */
		return (-EOPNOTSUPP);
	default:
		goto einval;
	}
      einval:
	return (-EINVAL);
}

STATIC __unlikely int
mx_pass_size(mx_pass_t * arg)
{
	int size = sizeof(*arg);

	size += arg->ctl_length;
	size += arg->dat_length;
	return (size);
}

noinline __unlikely int
mx_ioccpass(queue_t *q, mblk_t *mp, mblk_t *dp)
{
	return (-EOPNOTSUPP);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Test and Commit SL configuration settings
 *
 *  -------------------------------------------------------------------------
 */
#if 0
noinline __unlikely int
sl_test_config(struct xp *xp, sl_config_t * arg)
{
	return (-EOPNOTSUPP);
}

noinline __unlikely int
sl_commit_config(struct xp *xp, sl_config_t * arg)
{
	return (-EOPNOTSUPP);
}
#endif

noinline __unlikely void
sl_iocgoptions(struct ch *ch, mblk_t *dp)
{
	lmi_option_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->option;
}

noinline __unlikely int
sl_iocsoptions(struct ch *ch, mblk_t *dp)
{
	lmi_option_t *arg = (typeof(arg)) dp->b_rptr;

	ch->option = *arg;
	return (0);
}

noinline __unlikely void
sl_iocgconfig(struct ch *ch, mblk_t *dp)
{
	sl_config_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sl.config;
#if 1
	arg->t1 = drv_hztomsec(arg->t1);
	arg->t2 = drv_hztomsec(arg->t2);
	arg->t2l = drv_hztomsec(arg->t2l);
	arg->t2h = drv_hztomsec(arg->t2h);
	arg->t3 = drv_hztomsec(arg->t3);
	arg->t4n = drv_hztomsec(arg->t4n);
	arg->t4e = drv_hztomsec(arg->t4e);
	arg->t5 = drv_hztomsec(arg->t5);
	arg->t6 = drv_hztomsec(arg->t6);
	arg->t7 = drv_hztomsec(arg->t7);
#endif
}

noinline __unlikely int
sl_iocsconfig(struct ch *ch, mblk_t *dp)
{
	sl_config_t *arg = (typeof(arg)) dp->b_rptr;

#if 1
	arg->t1 = drv_msectohz(arg->t1);
	arg->t2 = drv_msectohz(arg->t2);
	arg->t2l = drv_msectohz(arg->t2l);
	arg->t2h = drv_msectohz(arg->t2h);
	arg->t3 = drv_msectohz(arg->t3);
	arg->t4n = drv_msectohz(arg->t4n);
	arg->t4e = drv_msectohz(arg->t4e);
	arg->t5 = drv_msectohz(arg->t5);
	arg->t6 = drv_msectohz(arg->t6);
	arg->t7 = drv_msectohz(arg->t7);
#endif
	ch->sl.config = *arg;
	return (0);
}

noinline __unlikely int
sl_ioctconfig(struct ch *ch, mblk_t *dp)
{
	sl_config_t *arg = (typeof(arg)) dp->b_rptr;

	(void) arg;
	return (-EOPNOTSUPP);
}

noinline __unlikely int
sl_ioccconfig(struct ch *ch, mblk_t *dp)
{
	sl_config_t *arg = (typeof(arg)) dp->b_rptr;

	(void) arg;
	return (-EOPNOTSUPP);
}

noinline __unlikely void
sl_iocgstatem(struct ch *ch, mblk_t *dp)
{
	sl_statem_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sl.statem;
}

noinline __unlikely int
sl_ioccmreset(struct ch *ch, mblk_t *dp)
{
	sl_statem_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sl.statem = *arg;
	return (0);
}

noinline __unlikely void
sl_iocgstatsp(struct ch *ch, mblk_t *dp)
{
	sl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sl.statsp;
}

noinline __unlikely int
sl_iocsstatsp(struct ch *ch, mblk_t *dp)
{
	sl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sl.statsp = *arg;
	return (0);
}

noinline __unlikely void
sl_iocgstats(struct ch *ch, mblk_t *dp)
{
	sl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sl.stats;
}

noinline __unlikely int
sl_ioccstats(struct ch *ch, mblk_t *dp)
{
	sl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	bzero(&ch->sl.stats, sizeof(ch->sl.stats));
	return (0);
}

noinline __unlikely void
sl_iocgnotify(struct ch *ch, mblk_t *dp)
{
	sl_notify_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sl.notify;
}

noinline __unlikely int
sl_iocsnotify(struct ch *ch, mblk_t *dp)
{
	sl_notify_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sl.notify.events |= arg->events;
	return (0);
}

noinline __unlikely int
sl_ioccnotify(struct ch *ch, mblk_t *dp)
{
	sl_notify_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sl.notify.events &= ~(arg->events);
	return (0);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Test and Commit SDT configuration settings
 *
 *  -------------------------------------------------------------------------
 */
noinline __unlikely int
sdt_test_config(struct ch *ch, sdt_config_t * arg)
{
	if (!arg->t8)
		arg->t8 = ch->sdt.config.t8;
	if (!arg->Tin)
		arg->Tin = ch->sdt.config.Tin;
	if (!arg->Tie)
		arg->Tie = ch->sdt.config.Tie;
	if (!arg->T)
		arg->T = ch->sdt.config.T;
	if (!arg->D)
		arg->D = ch->sdt.config.D;
	if (!arg->Te)
		arg->Te = ch->sdt.config.Te;
	if (!arg->De)
		arg->De = ch->sdt.config.De;
	if (!arg->Ue)
		arg->Ue = ch->sdt.config.Ue;
	if (!arg->N)
		arg->N = ch->sdt.config.N;
	if (!arg->m)
		arg->m = ch->sdt.config.m;
	if (!arg->b)
		arg->b = ch->sdt.config.b;
	else if (arg->b != ch->sdt.config.b)
		return (-EINVAL);
	return (0);
}

noinline __unlikely void
sdt_iocgoptions(struct ch *ch, mblk_t *dp)
{
	lmi_option_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->option;
}

noinline __unlikely int
sdt_iocsoptions(struct ch *ch, mblk_t *dp)
{
	lmi_option_t *arg = (typeof(arg)) dp->b_rptr;

	ch->option = *arg;
	return (0);
}

noinline __unlikely void
sdt_iocgconfig(struct ch *ch, mblk_t *dp)
{
	sdt_config_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdt.config;
#if 1
	arg->t8 = drv_hztomsec(arg->t8);
#endif
}

noinline __unlikely int
sdt_iocsconfig(struct ch *ch, mblk_t *dp)
{
	sdt_config_t *arg = (typeof(arg)) dp->b_rptr;

#if 1
	arg->t8 = drv_msectohz(arg->t8);
#endif
	ch->sdt.config = *arg;
	return (0);
}

noinline __unlikely int
sdt_ioctconfig(struct ch *ch, mblk_t *dp)
{
	sdt_config_t *arg = (typeof(arg)) dp->b_rptr;

	return sdt_test_config(ch, arg);
}

noinline __unlikely int
sdt_ioccconfig(struct ch *ch, mblk_t *dp)
{
	sdt_config_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sdt.config = *arg;
	return (0);
}

noinline __unlikely void
sdt_iocgstatem(struct ch *ch, mblk_t *dp)
{
	sdt_statem_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdt.statem;
}

noinline __unlikely int
sdt_ioccmreset(struct ch *ch, mblk_t *dp)
{
	fixme(("Master reset\n"));
	return (-EOPNOTSUPP);
}

noinline __unlikely void
sdt_iocgstatsp(struct ch *ch, mblk_t *dp)
{
	sdt_stats_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdt.statsp;
}

noinline __unlikely int
sdt_iocsstatsp(struct ch *ch, mblk_t *dp)
{
	sdt_stats_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sdt.statsp = *arg;
	return (0);
}

noinline __unlikely void
sdt_iocgstats(struct ch *ch, mblk_t *dp)
{
	sdt_stats_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdt.stats;
}

noinline __unlikely int
sdt_ioccstats(struct ch *ch, mblk_t *dp)
{
	sdt_stats_t *arg = (typeof(arg)) dp->b_rptr;

	bzero(&ch->sdt.stats, sizeof(ch->sdt.stats));
	return (0);
}

noinline __unlikely void
sdt_iocgnotify(struct ch *ch, mblk_t *dp)
{
	sdt_notify_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdt.notify;
}

noinline __unlikely int
sdt_iocsnotify(struct ch *ch, mblk_t *dp)
{
	sdt_notify_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sdt.notify = *arg;
	return (0);
}

noinline __unlikely int
sdt_ioccnotify(struct ch *ch, mblk_t *dp)
{
	sdt_notify_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sdt.notify.events &= ~arg->events;
	return (0);
}

noinline __unlikely void
sdt_ioccabort(struct ch *ch)
{
	ch->tx.residue |= 0x7f << ch->tx.rbits;
	ch->tx.rbits += 7;
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Test SDL configuration settings
 *
 *  -------------------------------------------------------------------------
 */
noinline __unlikely int
sdl_test_config(struct ch *ch, sdl_config_t * arg)
{
	if (arg->ifflags) {
		return (-EINVAL);
	}
	switch (arg->iftype) {
	case SDL_TYPE_NONE:	/* unknown/unspecified */
		arg->iftype = ch->sdl.config.iftype;
		break;
	case SDL_TYPE_DS0:	/* DS0 channel */
	case SDL_TYPE_DS0A:	/* DS0A channel */
		/* yes we allow DS0A on E1 */
		break;
	case SDL_TYPE_E1:	/* full E1 span */
		if (ch->sp->config.ifgtype != SDL_GTYPE_E1) {
			return (-EINVAL);
		}
		break;
	case SDL_TYPE_T1:	/* full T1 span */
		if (ch->sp->config.ifgtype != SDL_GTYPE_T1) {
			return (-EINVAL);
		}
		break;
	case SDL_TYPE_J1:	/* full J1 span */
		if (ch->sp->config.ifgtype != SDL_GTYPE_T1 && ch->sp->config.ifgtype != SDL_GTYPE_J1) {
			return (-EINVAL);
		}
		break;
	default:
		return (-EINVAL);
	}
	switch (arg->ifmode) {
	case SDL_MODE_NONE:	/* */
		arg->ifmode = ch->sdl.config.ifmode;
		break;
	case SDL_MODE_PEER:	/* */
		break;
	case SDL_MODE_ECHO:	/* */
	case SDL_MODE_REM_LB:	/* */
	case SDL_MODE_LOC_LB:	/* */
	case SDL_MODE_LB_ECHO:	/* */
	case SDL_MODE_TEST:	/* */
		break;
	default:
		return (-EINVAL);
	}
	switch (arg->ifgmode) {
	case SDL_GMODE_NONE:
	case SDL_GMODE_LOC_LB:
		break;
	case SDL_GMODE_REM_LB:
	default:
		return (-EINVAL);
	}
	if (ch->sp) {
		switch (arg->ifgtype) {
		case SDL_GTYPE_NONE:	/* */
			arg->ifgtype = ch->sp->config.ifgtype;
			break;
		case SDL_GTYPE_E1:	/* */
		case SDL_GTYPE_T1:	/* */
		case SDL_GTYPE_J1:	/* */
			if (arg->ifgtype != ch->sp->config.ifgtype) {
				return (-EINVAL);
			}
			break;
		default:
			return (-EINVAL);
		}
		switch (arg->ifgcrc) {
		case SDL_GCRC_NONE:	/* */
			switch (arg->ifgtype) {
			case SDL_GTYPE_E1:
				arg->ifgcrc = SDL_GCRC_CRC4;
				break;
			case SDL_GTYPE_T1:
				arg->ifgcrc = SDL_GCRC_CRC6;
				break;
			case SDL_GTYPE_J1:
				arg->ifgcrc = SDL_GCRC_CRC6J;
				break;
			default:
				return (-EINVAL);
			}
			break;
		case SDL_GCRC_CRC4:	/* */
			if (arg->ifgtype != SDL_GTYPE_E1) {
				return (-EINVAL);
			}
			break;
		case SDL_GCRC_CRC5:	/* */
			if (arg->ifgtype != SDL_GTYPE_E1) {
				return (-EINVAL);
			}
			break;
		case SDL_GCRC_CRC6:	/* */
			if (arg->ifgtype != SDL_GTYPE_T1) {
				return (-EINVAL);
			}
			break;
		case SDL_GCRC_CRC6J:
			if (arg->ifgtype != SDL_GTYPE_J1) {
				return (-EINVAL);
			}
			break;
		default:
			return (-EINVAL);
		}
		switch (arg->ifclock) {
		case SDL_CLOCK_NONE:	/* */
			arg->ifclock = ch->sp->config.ifclock;
			break;
		case SDL_CLOCK_INT:	/* */
		case SDL_CLOCK_MASTER:	/* */
		case SDL_CLOCK_EXT:	/* */
		case SDL_CLOCK_SLAVE:	/* */
		case SDL_CLOCK_LOOP:	/* */
			break;
		default:
			return (-EINVAL);
		}
		switch (arg->ifcoding) {
		case SDL_CODING_NONE:	/* */
			arg->ifcoding = ch->sp->config.ifcoding;
			break;
		case SDL_CODING_AMI:	/* */
			break;
		case SDL_CODING_B8ZS:	/* */
			if (arg->ifgtype != SDL_GTYPE_T1 && arg->ifgtype != SDL_GTYPE_J1) {
				return (-EINVAL);
			}
			break;
		case SDL_CODING_HDB3:	/* */
			if (arg->ifgtype != SDL_GTYPE_E1) {
				return (-EINVAL);
			}
			break;
		default:
		case SDL_CODING_B6ZS:	/* */
			return (-EINVAL);
		}
		switch (arg->ifframing) {
		case SDL_FRAMING_NONE:	/* */
			arg->ifframing = ch->sp->config.ifframing;
			break;
		case SDL_FRAMING_CCS:	/* */
		case SDL_FRAMING_CAS:	/* */
			if (arg->ifgtype != SDL_GTYPE_E1) {
				return (-EINVAL);
			}
			break;
		case SDL_FRAMING_SF:	/* */
		case SDL_FRAMING_ESF:	/* */
			if (arg->ifgtype != SDL_GTYPE_T1 && arg->ifgtype != SDL_GTYPE_J1) {
				return (-EINVAL);
			}
			break;
		default:
			return (-EINVAL);
		}
		if (arg->iftxlevel == 0)
			arg->iftxlevel = ch->sp->config.iftxlevel;
		else if (arg->iftxlevel <= 16)
			arg->iftxlevel--;
		else {
			return (-EINVAL);
		}
		if (ch->sp->cd) {
			int src;

			for (src = 0; src < SDL_SYNCS; src++)
				if (arg->ifsyncsrc[src] < 0 || arg->ifsyncsrc[src] > 4) {
					return (-EINVAL);
				}
		} else {
			return (-EINVAL);
		}
	} else {
		return (-EINVAL);
	}
	return (0);
}

/* 
 *  -------------------------------------------------------------------------
 *
 *  Commit SDL configuration settings
 *
 *  -------------------------------------------------------------------------
 */
noinline __unlikely void
sdl_commit_config(struct ch *ch, sdl_config_t * arg)
{
	int chan_reconfig = 0, span_reconfig = 0, card_reconfig = 0;
	struct sp *sp;
	struct cd *cd = NULL;

	if (ch->sdl.config.iftype != arg->iftype) {
		ch->sdl.config.iftype = arg->iftype;
		chan_reconfig = 1;
	}
	switch (arg->iftype) {
	case SDL_TYPE_DS0A:
		ch->sdl.config.ifrate = 56000;
		break;
	case SDL_TYPE_DS0:
		ch->sdl.config.ifrate = 64000;
		break;
	case SDL_TYPE_J1:
		ch->sdl.config.ifrate = 1544000;
		break;
	case SDL_TYPE_T1:
		ch->sdl.config.ifrate = 1544000;
		break;
	case SDL_TYPE_E1:
		ch->sdl.config.ifrate = 2048000;
		break;
	}
	if (ch->sdl.config.ifrate != arg->ifrate) {
		ch->sdl.config.ifrate = arg->ifrate;
		chan_reconfig = 1;
	}
	if (ch->sdl.config.ifmode != arg->ifmode) {
		ch->sdl.config.ifmode = arg->ifmode;
		chan_reconfig = 1;
	}
	if ((sp = ch->sp)) {
		int chan;
		struct ch *c;

		if (sp->config.ifgcrc != arg->ifgcrc) {
			for (chan = 0; chan < 32; chan++)
				if ((c = sp->chans[chan]))
					c->sdl.config.ifgcrc = arg->ifgcrc;
			sp->config.ifgcrc = arg->ifgcrc;
			span_reconfig = 1;
		}
		if (sp->config.ifgmode != arg->ifgmode) {
			for (chan = 0; chan < 32; chan++)
				if ((c = sp->chans[chan]))
					c->sdl.config.ifgmode = arg->ifgmode;
			sp->config.ifgmode = arg->ifgmode;
			span_reconfig = 1;
		}
		if (sp->config.ifclock != arg->ifclock) {
			for (chan = 0; chan < 32; chan++)
				if ((c = sp->chans[chan]))
					c->sdl.config.ifclock = arg->ifclock;
			sp->config.ifclock = arg->ifclock;
			span_reconfig = 1;
		}
		if (sp->config.ifcoding != arg->ifcoding) {
			for (chan = 0; chan < 32; chan++)
				if ((c = sp->chans[chan]))
					c->sdl.config.ifcoding = arg->ifcoding;
			sp->config.ifcoding = arg->ifcoding;
			span_reconfig = 1;
		}
		if (sp->config.ifframing != arg->ifframing) {
			for (chan = 0; chan < 32; chan++)
				if ((c = sp->chans[chan]))
					c->sdl.config.ifframing = arg->ifframing;
			sp->config.ifframing = arg->ifframing;
			span_reconfig = 1;
		}
		if (sp->config.iftxlevel != arg->iftxlevel) {
			for (chan = 0; chan < 32; chan++)
				if ((c = sp->chans[chan]))
					c->sdl.config.iftxlevel = arg->iftxlevel;
			sp->config.iftxlevel = arg->iftxlevel;
			span_reconfig = 1;
		}
		if ((cd = sp->cd)) {
			int src, span, chan;
			struct sp *s;
			struct ch *c;

			for (src = 0; src < SDL_SYNCS; src++) {
				if (cd->config.ifsyncsrc[src] == arg->ifsyncsrc[src])
					continue;
				for (span = 0; span < cd->ports; span++) {
					if (!(s = cd->spans[span]))
						continue;
					for (chan = 0; chan < 32; chan++) {
						if (!(c = s->chans[chan]))
							continue;
						c->sdl.config.ifsyncsrc[src] = arg->ifsyncsrc[src];
					}
				}
				cd->config.ifsyncsrc[src] = arg->ifsyncsrc[src];
				card_reconfig = 1;
			}
		}
	}
	if (sp && span_reconfig && (sp->config.ifflags & SDL_IF_UP) && cd) {
		/* need to bring up span */
		switch (cd->config.ifgtype) {
		case SDL_GTYPE_E1:
		{
			printd(("%s: performing reconfiguration of E1 span %d\n", DRV_NAME, sp->span));
			/* Tell ISR to re-evaluate the sync source */
			cd->eval_syncsrc = 1;
			xp_span_reconfig(sp);
			break;
		}
		case SDL_GTYPE_T1:
		{
			printd(("%s: performing reconfiguration of T1 span %d\n", DRV_NAME, sp->span));
			/* Tell ISR to re-evaluate the sync source */
			cd->eval_syncsrc = 1;
			xp_span_reconfig(sp);
			break;
		}
		case SDL_GTYPE_J1:
		{
			printd(("%s: performing reconfiguration of J1 span %d\n", DRV_NAME, sp->span));
			/* Tell ISR to re-evaluate the sync source */
			cd->eval_syncsrc = 1;
			xp_span_reconfig(sp);
			break;
		}
		default:
			swerr();
			break;
		}
	}
	if (cd && card_reconfig && (cd->config.ifflags & SDL_IF_UP)) {
		cd->eval_syncsrc = 1;
	}
}

/* 
 *  SDL_IOCGOPTIONS:    lmi_option_t
 *  -----------------------------------
 */
noinline __unlikely void
sdl_iocgoptions(struct ch *ch, mblk_t *dp)
{
	lmi_option_t *arg = (lmi_option_t *) dp->b_rptr;

	*arg = ch->option;
}

/* 
 *  SDL_IOCSOPTIONS:    lmi_option_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_iocsoptions(struct ch *ch, mblk_t *dp)
{
	lmi_option_t *arg = (typeof(arg)) dp->b_rptr;

	ch->option = *arg;
	return (0);
}

/* 
 *  SDL_IOCGCONFIG:     sdl_config_t
 *  -----------------------------------
 */
noinline __unlikely void
sdl_iocgconfig(struct ch *ch, mblk_t *dp)
{
	struct cd *cd;
	struct sp *sp;
	int src;

	sdl_config_t *arg = (typeof(arg)) dp->b_rptr;

	bzero(arg, sizeof(*arg));

	arg->ifflags = ch->sdl.config.ifflags;
	arg->iftype = ch->sdl.config.iftype;
	arg->ifrate = ch->sdl.config.ifrate;
	arg->ifmode = ch->sdl.config.ifmode;
	arg->ifblksize = ch->sdl.config.ifblksize;
	if ((sp = ch->sp)) {
		arg->ifgtype = sp->config.ifgtype;
		arg->ifgrate = sp->config.ifgrate;
		arg->ifgmode = sp->config.ifgmode;
		arg->ifgcrc = sp->config.ifgcrc;
		arg->ifclock = sp->config.ifclock;
		arg->ifcoding = sp->config.ifcoding;
		arg->ifframing = sp->config.ifframing;
		arg->ifalarms = sp->config.ifalarms;
		arg->ifrxlevel = sp->config.ifrxlevel;
		arg->iftxlevel = sp->config.iftxlevel + 1;
		if ((cd = sp->cd)) {
			for (src = 0; src < SDL_SYNCS; src++)
				arg->ifsyncsrc[src] = cd->config.ifsyncsrc[src];
			arg->ifsync = cd->config.ifsync;
		}
	}
}

/* 
 *  SDL_IOCSCONFIG:     sdl_config_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_iocsconfig(struct ch *ch, mblk_t *dp)
{
	sdl_config_t *arg = (typeof(arg)) dp->b_rptr;
	int ret;

	if ((ret = sdl_test_config(ch, arg)))
		return (ret);
	sdl_commit_config(ch, arg);
	return (0);
}

/* 
 *  SDL_IOCTCONFIG:     sdl_config_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_ioctconfig(struct ch *ch, mblk_t *dp)
{
	sdl_config_t *arg = (typeof(arg)) dp->b_rptr;

	return sdl_test_config(ch, arg);
}

/* 
 *  SDL_IOCCCONFIG:     sdl_config_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_ioccconfig(struct ch *ch, mblk_t *dp)
{
	sdl_config_t *arg = (typeof(arg)) dp->b_rptr;

	sdl_commit_config(ch, arg);
	return (0);
}

/* 
 *  SDL_IOCGSTATEM:     sdl_statem_t
 *  -----------------------------------
 */
noinline __unlikely void
sdl_iocgstatem(struct ch *ch, mblk_t *dp)
{
	sdl_statem_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdl.statem;
}

/* 
 *  SDL_IOCCMRESET:     sdl_statem_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_ioccmreset(struct ch *ch, mblk_t *dp)
{
	(void) ch;
	(void) dp;
	fixme(("FIXME: Support master reset\n"));
	return (-EOPNOTSUPP);
}

/* 
 *  SDL_IOCGSTATSP:     sdl_stats_t
 *  -----------------------------------
 */
noinline __unlikely void
sdl_iocgstatsp(struct ch *ch, mblk_t *dp)
{
	sdl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdl.statsp;
}

/* 
 *  SDL_IOCSSTATSP:     sdl_stats_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_iocsstatsp(struct ch *ch, mblk_t *dp)
{
	sdl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	fixme(("FIXME: check these settings\n"));
	ch->sdl.statsp = *arg;
	return (0);
}

/* 
 *  SDL_IOCGSTATS:      sdl_stats_t
 *  -----------------------------------
 */
noinline __unlikely void
sdl_iocgstats(struct ch *ch, mblk_t *dp)
{
	sdl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdl.stats;
}

/* 
 *  SDL_IOCCSTATS:      sdl_stats_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_ioccstats(struct ch *ch, mblk_t *dp)
{
	sdl_stats_t *arg = (typeof(arg)) dp->b_rptr;

	bzero(&ch->sdl.stats, sizeof(ch->sdl.stats));
	return (0);
}

/* 
 *  SDL_IOCGNOTIFY:     sdl_notify_t
 *  -----------------------------------
 */
noinline __unlikely void
sdl_iocgnotify(struct ch *ch, mblk_t *dp)
{
	sdl_notify_t *arg = (typeof(arg)) dp->b_rptr;

	*arg = ch->sdl.notify;
}

/* 
 *  SDL_IOCSNOTIFY:     sdl_notify_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_iocsnotify(struct ch *ch, mblk_t *dp)
{
	sdl_notify_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sdl.notify.events |= arg->events;
	return (0);
}

/* 
 *  SDL_IOCCNOTIFY:     sdl_notify_t
 *  -----------------------------------
 */
noinline __unlikely int
sdl_ioccnotify(struct ch *ch, mblk_t *dp)
{
	sdl_notify_t *arg = (typeof(arg)) dp->b_rptr;

	ch->sdl.notify.events &= ~arg->events;
	return (0);
}

/* 
 *  SDL_IOCCDISCTX:     
 *  -----------------------------------
 */
noinline __unlikely void
sdl_ioccdisctx(struct ch *ch)
{
	ch->sdl.config.ifflags &= ~SDL_IF_TX_RUNNING;
}

/* 
 *  SDL_IOCCONNTX:      
 *  -----------------------------------
 */
noinline __unlikely void
sdl_ioccconntx(struct ch *ch)
{
	ch->sdl.config.ifflags |= SDL_IF_TX_RUNNING;
}

/*
 *  NIT input-output controls
 *  -------------------------------------------------------------------------
 */

/* Parse out the interface name and place the card, span and chan in the xp structure.  Return 0 on
 * success, negative error code on error. */
int
parse_interface(struct xp *xp, char *p, size_t len)
{
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	char *e = p + len;

	xp->card = -1;
	xp->span = -1;
	xp->chan = -1;
	xp->monitor = XP_MONITOR_NONE;
	xp->level = XP_LEVEL_MON_SDT;

	if (p + strlen(DRV_NAME) > e)
		return (-EINVAL);
	if (strstr(p, DRV_NAME) != p)
		return (-EINVAL);
	p += strlen(DRV_NAME);
	if (p >= e || *p == '\0') {
		xp->monitor = XP_MONITOR_GLOB;
		xp->level = XP_LEVEL_MON_SDT;
		xp->xray.flags = XP_XRAY_SDT_GLOB;
		return (0);
	}
	xp->card = 0;
	while (p < e && '0' <= *p && *p <= '9') {
		xp->card *= 10;
		xp->card += *p - '0';
		p++;
	}
	xp->ppa = ((0xff << 0) | (0x0f << 8) | ((xp->card & 0x0f) << 12));
	if (xp->card >= 0x0f || (cd = cd_find(xp->ppa)) == NULL)
		return (-ENXIO);
	if (p >= e || *p == '\0') {
		xp->monitor = XP_MONITOR_CARD;
		xp->level = XP_LEVEL_MON_SDT;
		xp->xray.flags = XP_XRAY_SDT_CARD;
		return (0);
	}
	if (*p != '.')
		return (-EINVAL);
	p++;
	xp->span = 0;
	while (p < e && '0' <= *p && *p <= '9') {
		xp->span *= 10;
		xp->span += *p - '0';
		p++;
	}
	xp->ppa = ((0xff << 0) | ((xp->span & 0x0f) << 8) | ((xp->card & 0x0f) << 12));
	if (xp->span >= cd->ports || (sp = sp_find(xp->ppa)) != NULL)
		return (-ENXIO);
	if (p >= e || *p == '\0') {
		xp->monitor = XP_MONITOR_SPAN;
		xp->level = XP_LEVEL_MON_SDT;
		xp->xray.flags = XP_XRAY_SDT_SPAN;
		return (0);
	}
	if (*p != ':')
		return (-EINVAL);
	p++;
	xp->chan = 0;
	while (p < e && '0' <= *p && *p <= '9') {
		xp->chan *= 10;
		xp->chan += *p - '0';
		p++;
	}
	xp->ppa = ((xp->chan & 0xff) | ((xp->span & 0x0f) << 8) | ((xp->card & 0x0f) << 12));
	if (xp->chan >= 32 || IS_ERR(ch = ch_find(xp->ppa)))
		return (-ENXIO);
	if (p >= e || *p == '\0') {
		xp->monitor = XP_MONITOR_CHAN;
		xp->level = XP_LEVEL_MON_SDT;
		xp->xray.flags = XP_XRAY_SDT_CHAN;
		return (0);
	}
	return (-EINVAL);

}

/** nit_niocbind: - process NIOCBIND ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See nit_if(4).  The NIOCBIND input-output control command will attach the stream represented by
  * its first argument to the network interface designated by its third argument, which should be a
  * pointer to an ifreq structure whose ifr_name field names the desired interface.  See <net/if.h>
  * for the definition of this structure.
  */
noinline __unlikely int
nit_niocbind(struct xp *xp, mblk_t *dp)
{
	struct ifreq *ifr = (typeof(ifr)) dp->b_rptr;
	int ret;

	if ((ret = parse_interface(xp, ifr->ifr_name, IFNAMSIZ)) == 0) {
		/* FIXME: initialize all capture variables */
		xp->xray.bpf.dlt = DLT_MTP2;
	}
	return (ret);
}

/** nit_niocssnap: - process NIOCSSNAP ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See nit_if(4).  The NIOCSSNAP input-output control command sets the current snapshot length to
  * the value given in the u_long pointed to by the input-output control command's final argument.
  * nit_if(4) interprets a snapshot length of zero as meaning infinity, so that it will copy all
  * selected packets in their entirety.  It constrains positive snapshot lengths to be at least the
  * length of an Ethernet header, so that it will pass at least the link-level header of all
  * selected packets to its upstream neighbor.
  */
noinline __unlikely int
nit_niocssnap(struct xp *xp, mblk_t *dp)
{
	ulong snap = *(ulong *) dp->b_rptr;

	if (snap != 0) {
		struct ch *ch;

		if ((ch = xp->ch) != NULL) {
			if (snap < ch->sdt.config.m + 1)
				return (-ERANGE);
		} else {
			if (snap < 7)
				return (-ERANGE);
		}
	}
	xp->xray.nit.snap = snap;
	return (0);
}

#ifdef __LP64__
/** nit_niocssnap32: - process 32-bit NIOCSSNAP ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See nit_if(4).  The NIOCSSNAP input-output control command sets the current snapshot length to
  * the value given in the u_long pointed to by the input-output control command's final argument.
  * nit_if(4) interprets a snapshot length of zero as meaning infinity, so that it will copy all
  * selected packets in their entirety.  It constrains positive snapshot lengths to be at least the
  * length of an Ethernet header, so that it will pass at least the link-level header of all
  * selected packets to its upstream neighbor.
  */
noinline __unlikely int
nit_niocssnap32(struct xp *xp, mblk_t *dp)
{
	ulong snap = *(uint32_t *) dp->b_rptr;

	if (snap != 0) {
		struct ch *ch;

		if ((ch = xp->ch) != NULL) {
			if (snap < ch->sdt.config.m + 1)
				return (-ERANGE);
		} else {
			if (snap < 7)
				return (-ERANGE);
		}
	}
	xp->xray.nit.snap = snap;
	return (0);
}
#endif				/* __LP64__ */

/** nit_niocgsnap: - process NIOCGSNAP ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDDATA message
  * @xp: private structure
  *
  * See nit_if(4).  The NIOCGSNAP input-output control command returns the snapshot length for this
  * device instance in the u_lonog pointed to by the input-output control command's final argument.
  */
noinline __unlikely int
nit_niocgsnap(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(ulong), 0)))
		return (-ENOSR);

	*(ulong *)db->b_rptr = xp->xray.nit.snap;
	return (0);
}

#ifdef __LP64__
/** nit_niocgsnap32: - process 32-bit NIOCGSNAP ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDDATA message
  * @xp: private structure
  *
  * See nit_if(4).  The NIOCGSNAP input-output control command returns the snapshot length for this
  * device instance in the u_lonog pointed to by the input-output control command's final argument.
  */
noinline __unlikely int
nit_niocgsnap32(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint32_t), 0)))
		return (-ENOSR);

	*(uint32_t *)db->b_rptr = xp->xray.nit.snap;
	return (0);
}
#endif				/* __LP64__ */

/** nit_niocsflags: - process NIOCSFLAGS ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See nit_if(4).  THe NIOCSFLAGS input-output control command recognizes the following flag bits,
  * which must be given in the u_long pointed to by the input-output control command's final
  * argument.  This set may be augmented in future releases (hah!).  All but the NI_PROMISC bit
  * control the addition of headers that precede the packet body.  These headers appear in the order
  * given below, with the last-mentioned enabled header adjacent to the packet body.
  *
  * NI_PROMISC
  *	Requets that the underlying interface be set into promiscuous mode and that all packets that
  *	the interface receives be passed up through the stream.  nit only honors this it for the
  *	super-user.
  * NI_TIMESTAMP
  *	Prepend to each selected packet a header containing the packet arrival time expressed as a
  *	struct timeval.
  * NI_DROPS
  *	Prepend to each selected packet a header containing the cumulative number of packets that
  *	this instance of nit has dropped because of flow control requirements or resource
  *	exhaustion.  The header value is expressed as u_long.  Note: it accounts only for events
  *	ocurring within nit, and does not count packets dropped at the network interface level or by
  *	upstream modules.
  * NI_LEN
  *	Prepend to each selected packet a headere containing the packet's original length
  *	(includeing link-level header), as it was before being trimmed to the snapshot length.  The
  *	header value is expressed as u_long.
  */
noinline __unlikely int
nit_niocsflags(struct xp *xp, mblk_t *dp)
{
	uint val = *(ulong *) dp->b_rptr;

	/* Note we would check super-user for NI_PROMISC but in this driver NI_PROMISC does nothing.  */
	if ((val & ~NI_USERBITS))
		return (-EINVAL);
	xp->xray.nit.flags = (xp->xray.nit.flags & ~NI_USERBITS) | val;
	return (0);
}

#ifdef __LP64__
/** nit_niocsflags32: - process 32-bit NIOCSFLAGS ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  */
noinline __unlikely int
nit_niocsflags32(struct xp *xp, mblk_t *dp)
{
	uint val = *(uint32_t *) dp->b_rptr;

	/* Note we would check super-user for NI_PROMISC but in this driver NI_PROMISC does nothing.  */
	if ((val & ~NI_USERBITS))
		return (-EINVAL);
	xp->xray.nit.flags = (xp->xray.nit.flags & ~NI_USERBITS) | val;
	return (0);
}
#endif				/* __LP64__ */

/** nit_niocgflags: - process NIOCGFLAGS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See nit_if(4).  Returns the current state of the flag bits for this device instance in the
  * u_long pointed to by the input-outpu control command's final argument.
  */
noinline __unlikely int
nit_niocgflags(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(ulong), 0)))
		return (-ENOSR);

	*(ulong *)db->b_rptr = xp->xray.nit.flags & NI_USERBITS;
	return (0);
}

#ifdef __LP64__
/** nit_niocgflags32: - process 32-bit NIOCGFLAGS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See nit_if(4).  Returns the current state of the flag bits for this device instance in the
  * u_long pointed to by the input-outpu control command's final argument.
  */
noinline __unlikely int
nit_niocgflags32(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint32_t), 0)))
		return (-ENOSR);

	*(uint32_t *)db->b_rptr = xp->xray.nit.flags & NI_USERBITS;
	return (0);
}
#endif				/* __LP64__ */

/*
 *  BPF input-output controls
 *  -------------------------------------------------------------------------
 */

/** bpf_biocgdlt: - process BIOCGDLT ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4). (uint) Returns the type of data link layer underlying the attached interface.
  * [EINVAL] is returned if no interface has yet been specified with BIOCSETIF or BIOCSETLIF.  The
  * device types, prefixed with "DLT_", are defined in <net/bpf.h>.  The (third) argument to
  * ioctl(2s) is a pointer to an unsigned int into which to place the value of the data link type.
  *
  * (4.4BSD) This is one of the original commands from the 4.4BSD implementation set.
  *
  * Under Linux Fast-STREAMS, the bpfdrv must support this command so that the capture application
  * can determine the data link type of the capture.
  */
noinline __unlikely int
bpf_biocgdlt(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (xp->ch == NULL)
		return (-EINVAL);

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
		return (-ENOSR);

	*(uint *) db->b_rptr = xp->xray.bpf.dlt;
	return (0);
}

/** bpf_biocsdlt: - process BIOCSDLT ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4). (uint) Use to specify the type of data link layer of the interface attached to
  * the bpf descriptor.  If the current interface is not of the given type, then the descriptor will
  * be reattached to an interface of the given type.  If the descriptor has promiscuous mode set,
  * the new interface will be moved to the promiscuous mode.  [EINVAL] is returned if no interface
  * has been specified with BIOCSETIF or BIOCSETLIF.  The device types, prefixed with "DLT_", are
  * defined in <net/bpf.h>.  The (third) argument to ioctl(2s) is a pointer to an unsigned int that
  * provides the requested data link type.
  *
  * (NetBSD, Not in 4.4BSD).  This is one of the original commands from the extended 4.4BSD
  * implementation set and is provided by all recent implementations.
  *
  * Under Linux Fast-STREAMS, the bpfdrv must support this command so that the capture application
  * can select the data link type of the capture.
  *
  * There are three data link types that are applicable for SS7 signalling links as follows:
  *
  *	DLT_MTP2_WITH_PHDR
  *	    The full HDLC frame with a link level pseudo-header prepended that provides the
  *	    signalling data link identifier and a flag indicating whether this frame is an extended
  *	    sequence number (Q.703/Annex A) frame.  This should be the default for capturing on
  *	    wildcard interfaces for BPF.
  *
  *	DLT_MTP2
  *	    The full HDLC frame with no link level pseudo-header.  It is impossible to tell from
  *	    which link each message comes and whether it is Annex A or not.  Therefore, maybe this
  *	    should not even be offerred for wildcard links but only specific slot interfaces.
  *
  *	DLT_MTP3
  *	    MSUs only with the MTP Level 2 header (BIB/BSN,FIB/FSN,LI) removed.  This is problematic
  *	    for Japanese SS7 because the message priority is encodeded in the LI spare bits, which
  *	    are discarded.  Unfortunately BPF has no idea about this.
  *
  * The most useful data link type for wildcard interfaces is DLT_MTP2_WITH_PHDR and it will be the
  * default for wildcard interfaces.  The most useful data link type for individual signalling links
  * is DLT_MTP2 (because the pseudo-header would be redundant on every frame) or even DLT_MTP3.
  */
noinline __unlikely int
bpf_biocsdlt(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	uint dlt;

	/* Check whether an interface has been set yet. */
	if (xp->monitor == XP_MONITOR_NONE)
		return (-EINVAL);

	switch ((dlt = *(uint *) dp->b_rptr)) {
	case DLT_MTP2_WITH_PHDR:
	case DLT_MTP2:
	case DLT_MTP3:
		if (xp->xray.bpf.dlt != dlt) {
			mblk_t *b;

			/* header changed, flush queue and clear stats */
			if (!(b = allocb(2, BPRI_MED)))
				return (-ENOSR);

			DB_TYPE(b) = M_FLUSH;
			b->b_wptr = b->b_rptr + 2;
			b->b_rptr[0] = FLUSHR;
			b->b_rptr[1] = 0;
			flushq(RD(q), FLUSHDATA);
			xp->xray.bpf.stats.bs_recv = 0;
			xp->xray.bpf.stats.bs_drop = 0;
			xp->xray.bpf.stats.bs_capt = 0;
			xp->xray.bpf.dlt = dlt;
			qreply(q, b);
		}
		xp->xray.bpf.dlt = dlt;
		return (0);
	default:
		return (-EINVAL);
	}
}

/** bpf_biocgdltlist: - process BIOCGDLTLIST ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure pointer
  * @cp: copyresp structure pointer
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4). (struct bpf_dltlist)  Retuns a list of data link types of the given interface.  A
  * user allocated buffer to hold the list and bpf_dltlist structure, defined in <net/bpf.h>.
  * [EINVAL] is returned if no interface has been specified.  The device typyes, prefixed with
  * "DLT_", are defined in <net/bpf.h>.  The (third) argument to ioctl(2s) is a pointer to a
  * bpf_dltlist structure that will contain the available data link types.  The bpf_dltlist
  * structure is defined as follows:
  *
  * struct bpf_dltlist {
  *	uint bfl_len;
  *	uint *bfl_list;
  * };
  *
  * The bpf_dltlist structure contains the following members:
  *
  * bfl_len	The unsigned integer number of array elements in the array pointed to by the
  *	        bfl_list member.  On call, this is the size of the array pointed to by the bfl_list
  *	        member.  On return, this is the number of elements in the array that where populated
  *	        by the driver.
  *
  * bfl_list	Points to an array of unsigned integers that wil contain the available data link
  *		types.  On call, this array must contain at least bfl_len elements.  On return, the
  *		array will contain bfl_len valid elements.  All other elements in the array will
  *		contain the same values as were provided on call.  Because there is no data link
  *		type value of zero (0), the caller should zero the entire array before the call as
  *		another mechanism to detect the number of elements returned.
  *
  * (NetBSD, Not in 4.4BSD)  This is one of the original commands from the extended 4.4BSD
  * implementation set and is provided by all recent implementations.
  *
  * Under Linux Fast-STREAMS, the bpfdrv must support this command so that capture applications can
  * determine the possible data link types for capture.
  *
  * For SS7 the three levels DLT_MTP2_WITH_PHDR, DLT_MTP2 and DLT_MTP3 are always available.  The
  * default will depend on whether it is a wildcard interface or a specific interface.  Wildcard
  * interfaces default to DLT_MTP2_WITH_PHDR, whereas, specific interfaces might default to DLT_MTP2
  * or DLT_MTP3.  Nevertheless, the capture application can set whichever DLT it wishes of these
  * three, so always report the three.
  */
noinline __unlikely int
bpf_biocgdltlist(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	caddr_t uaddr;
	uint n;

#if !defined __LP64__
	(void) cp;
#else				/* defined __LP64__ */
	if (cp->cp_flag == IOC_ILP32) {
		struct bpf_dltlist32 *bfl = (typeof(bfl)) dp->b_rptr;

		uaddr = (caddr_t) (ulong) bfl->bfl_list;
		n = bfl->bfl_len;
	} else
#endif				/* defined __LP64__ */
	{
		struct bpf_dltlist *bfl = (typeof(bfl)) dp->b_rptr;

		uaddr = (caddr_t) bfl->bfl_list;
		n = bfl->bfl_len;
	}

	if (n > 0) {
		mblk_t *db;

		if (n > 3)
			n = 3;

		if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
			return (-ENOSR);

		*(uint *) db->b_rptr = n;

		if (!(db = mi_copyout_alloc(q, mp, uaddr, n * sizeof(uint), 0)))
			return (-ENOSR);

		db->b_wptr = db->b_rptr;
		if (n) {
			*(uint *) db->b_wptr = DLT_MTP2_WITH_PHDR;
			db->b_wptr += sizeof(uint);
			n--;
		}
		if (n) {
			*(uint *) db->b_wptr = DLT_MTP2;
			db->b_wptr += sizeof(uint);
			n--;
		}
		if (n) {
			*(uint *) db->b_wptr = DLT_MTP3;
			db->b_wptr += sizeof(uint);
			n--;
		}
	}
	mi_copy_set_rval(mp, 3);
	return (0);
}

/** bpf_biocpromisc: - process the BIOCPROMISC ioctl
  * @xp: private structure
  *
  * See bpfdrv(4).  (none)  Force the interface into promiscuous mode.  All packets, not just those
  * destined for the local host, are processed.  Since more than one file can be listening on a
  * given interface, a listener that opened its interface non-promiscuously may still receive
  * packets promiscuously.  This problem can be remedied with an appropriate filter.  The (third)
  * argument to ioctl(2s) is ignored.
  *
  * (4.4BSD) This is one of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, the bpfdrv must support this command if promiscuous captuer is permitted.  The
  * response in the case that promiscuous capture is not permitted is not well documented.
  *
  * This driver does not provide for promiscuous modes, so the flag is simply accepted.
  */
noinline __unlikely int
bpf_biocpromisc(struct xp *xp)
{
	xp->xray.bpf.flags |= BPF_PROMISC;
	return (0);
}

/** bpf_biocflush: - process the BIOCFLUSH ioctl
  * @q: write queue
  * @xp: private structure
  *
  * See bpfdrv(4).  (none) Flushes the buffer of incoming packets, and resets the statistics that
  * are returned by BIOCGSTATS and BIOCGSTATSOLD.  The equivalent function can be performed using
  * the I_FLUSH(7) STREAMS command.  The (third) argument to ioctl(2s) is ignored.
  *
  * (4.4BSD) This is one of the original commands from the 4.4BSD implementaiton set.
  *
  * Under Linux Fast-STREAMS, the bpfdrv must support this command.  The response to the command is
  * to flush its own read queue and reset its counts and issue a M_FLUSH(9) message upstream
  * flushing the read queues so that the equivalent action is performed by the bpfmod(4) module and
  * the Stream head.  bpfmod(4) expects that the driver will issue a M_FLUSH(9) message upstream to
  * flush the read queue when the driver receives this command.
  */
noinline __unlikely int
bpf_biocflush(queue_t *q, struct xp *xp)
{
	mblk_t *bp;

	if ((bp = allocb(2, BPRI_MED)) == NULL)
		return (-ENOSR);

	DB_TYPE(bp) = M_FLUSH;
	bp->b_wptr = bp->b_rptr + 2;
	bp->b_rptr[0] = FLUSHR;
	bp->b_rptr[1] = 0;
	flushq(RD(q), FLUSHDATA);
	/* zero statistics */
	xp->xray.bpf.stats.bs_recv = 0;
	xp->xray.bpf.stats.bs_drop = 0;
	xp->xray.bpf.stats.bs_capt = 0;
	qreply(q, bp);
	return (0);
}

/** bpf_biocgetif: - process the BIOCGETIF ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (struct ifreq)  Returns the name of the hardware interface that the file is
  * listening on.  The name is returned in the ifr_name field of the ifreq structure.  All other
  * fileds are undefined.  The (third) argument to ioctl(2s) is a pointer to an ifreq structure.
  * The ifreq stsructure is defined in <net/if.h> and documented in netdevice(7).  Only the ifr_name
  * field of the structure is used: all other fields are undefined.
  *
  * (4.4BSD) This is one of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, the bpfdrv may support this command to permit the capture application to determine
  * the current interface setting.  (It is not that important, because it will return [EINVAL] if it
  * has not already been set by the application.)
  */
noinline __unlikely int
bpf_biocgetif(queue_t *q, mblk_t *mp, struct xp *xp)
{
	struct ifreq *ifr;
	mblk_t *db;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*ifr), 0))) {
		ifr = (typeof(ifr)) db->b_rptr;
		bzero(ifr, sizeof(*ifr));
		switch (xp->monitor) {
		default:
		case XP_MONITOR_NONE:
			return (-ENXIO);
		case XP_MONITOR_GLOB:
			snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME);
			break;
		case XP_MONITOR_CARD:
			snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d", xp->card);
			break;
		case XP_MONITOR_SPAN:
			snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d.%d", xp->card, xp->span);
			break;
		case XP_MONITOR_CHAN:
			snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d.%d:%d", xp->card, xp->span, xp->chan);
			break;
		}
		return (0);
	}
	return (-ENOSR);
}

/** bpf_biocsetif: - process BIOCSETIF ioctl
  * @q: write queue
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct ifreq)  Sets the hardware interface associated with the file.  This
  * command must be performed before any packets can be read.  The device is indicated by name using
  * the ifr_name field of the ifreq structure.  Additionally, the actions of BIOCFLUSH are
  * performed.  The (third) argument to ioctl(2s) is a pointer to an ifreq structure.  The ifreq
  * structure is defined in <net/if.h> and documented in netdevice(7).  Only the ifr_name field of
  * the structure is used: all other fields are undefined.
  *
  * (4.4BSD)  This is one of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, the bpfdrv must support this command to permit capture to occur at all.  When this
  * command is issued, the driver should attach, bind and enable capture on a valid interface.  When
  * the interface was previously set, the driver should first disable, unbind and detach the old
  * interface.  Whether this normally moves the interface to the IFF_UP or down state is not well
  * documented.
  */
noinline __unlikely int
bpf_biocsetif(queue_t *q, struct xp *xp, mblk_t *dp)
{
	struct ifreq *ifr = (typeof(ifr)) dp->b_rptr;
	mblk_t *bp;
	int err;

	if ((bp = allocb(2, BPRI_MED)) == NULL)
		return (-ENOSR);

	if ((err = parse_interface(xp, ifr->ifr_name, IFNAMSIZ)) != 0) {
		freeb(bp);
		return (err);
	}
	/* FIXME: initialize all capture variables */
	xp->xray.bpf.dlt = DLT_MTP2_WITH_PHDR;

	DB_TYPE(bp) = M_FLUSH;
	bp->b_wptr = bp->b_rptr + 2;
	bp->b_rptr[0] = FLUSHR;
	bp->b_rptr[1] = 0;
	flushq(RD(q), FLUSHDATA);
	/* zero statistics */
	xp->xray.bpf.stats.bs_recv = 0;
	xp->xray.bpf.stats.bs_drop = 0;
	xp->xray.bpf.stats.bs_capt = 0;
	qreply(q, bp);

	return (err);
}

/** bpf_biocgetlif: - process the BIOCGETLIF ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (struct lifreq)  Returns the name of the hardware interface that the file is
  * listening on.  The name is returned in the ifr_name field of the lifreq structure.  All other
  * fileds are undefined.  The (third) argument to ioctl(2s) is a pointer to an lifreq structure.
  * The lifreq stsructure is defined in <net/if.h> and documented in netdevice(7).  Only the
  * ifr_name field of the structure is used: all other fields are undefined.
  *
  * (Solaris only.) These are Solaris-only commands (because only Solaris has the lifreq structure).
  * Under Linux Fast-STREAMS, these commands are implemented by bpfdrv(4) for compatibility and to
  * allow 32-character instead of 16-character interface names.
  */
noinline __unlikely int
bpf_biocgetlif(queue_t *q, mblk_t *mp, struct xp *xp)
{
	struct lifreq *lifr;
	mblk_t *db;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*lifr), 0)))
		return (-ENOSR);

	lifr = (typeof(lifr)) db->b_rptr;
	bzero(lifr, sizeof(*lifr));
	switch (xp->monitor) {
	default:
	case XP_MONITOR_NONE:
		return (-ENXIO);
	case XP_MONITOR_GLOB:
		snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME);
		break;
	case XP_MONITOR_CARD:
		snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d", xp->card);
		break;
	case XP_MONITOR_SPAN:
		snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d.%d", xp->card, xp->span);
		break;
	case XP_MONITOR_CHAN:
		snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d.%d:%d", xp->card, xp->span, xp->chan);
		break;
	}
	return (0);
}

/** bpf_biocsetlif: - process BIOCSETLIF ioctl
  * @q: write queue
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct lifreq)  Sets the hardware interface associated with the file.  This
  * command must be performed before any packets can be read.  The device is indicated by name using
  * the ifr_name field of the lifreq structure.  Additionally, the actions of BIOCFLUSH are
  * performed.  The (third) argument to ioctl(2s) is a pointer to an lifreq structure.  The lifreq
  * structure is defined in <net/if.h> and documented in netdevice(7).  Only the ifr_name field of
  * the structure is used: all other fields are undefined.
  *
  * (Solaris only.) These are Solaris-only commands (because only Solaris has the lifreq structure).
  * Under Linux Fast-STREAMS, these commands are implemented by bpfdrv(4) for compatibility and to
  * allow 32-character instead of 16-character interface names.
  */
noinline __unlikely int
bpf_biocsetlif(queue_t *q, struct xp *xp, mblk_t *dp)
{
	struct lifreq *lifr = (typeof(lifr)) dp->b_rptr;
	mblk_t *bp;
	int err;

	if ((bp = allocb(2, BPRI_MED)) == NULL)
		return (-ENOSR);

	if ((err = parse_interface(xp, lifr->lifr_name, LIFNAMSIZ)) != 0) {
		freeb(bp);
		return (err);
	}
	/* FIXME: initialize all capture variables */
	xp->xray.bpf.dlt = DLT_MTP2_WITH_PHDR;

	DB_TYPE(bp) = M_FLUSH;
	bp->b_wptr = bp->b_rptr + 2;
	bp->b_rptr[0] = FLUSHR;
	bp->b_rptr[1] = 0;
	flushq(RD(q), FLUSHDATA);
	/* zero statistics */
	xp->xray.bpf.stats.bs_recv = 0;
	xp->xray.bpf.stats.bs_drop = 0;
	xp->xray.bpf.stats.bs_capt = 0;
	qreply(q, bp);

	return (err);
}

/** bpf_biocgstats: - process BIOCGSTATS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (struct bpf_stat)  Returns the bpf statistics structure.  This is a newer
  * structure that contains 64-bit counts and also calculates the number of packets captured after
  * filtering.
  *
  * (NetBSD, OpenBSD, Solaris.)  Many recent implementations (FreeBSD being the notable exception)
  * implement the 64-bit extended statistics includeing the bs_capt field.  The original 4.4BSD
  * command is renamed to BIOCGSTATSOLD.  Under Linux Fast-STREAMS, this command is implemented by
  * both bpfdrv(4) and bpfmod(4).  bpfmod(4) will intercept the command on its way from the driver
  * and will add its own counts into the statistics.  It adds its drops to those of the driver in
  * bs_drop, and sets the numer of packets that passed the filter in bs_capt.
  */
noinline __unlikely int
bpf_biocgstats(queue_t *q, mblk_t *mp, struct xp *xp)
{
	struct bpf_stat *bs;
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(*bs), 0)))
		return (-ENOSR);

	bs = (typeof(bs)) db->b_rptr;
	bzero(bs, sizeof(*bs));
	bs->bs_recv = xp->xray.bpf.stats.bs_recv;
	bs->bs_drop = xp->xray.bpf.stats.bs_drop;
	bs->bs_capt = xp->xray.bpf.stats.bs_capt;
	return (0);
}

/** bpf_biocgstatsold: - process BIOCGSTATSOLD ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (struct bpf_stat_old)  Returns the bpf statistics structure.  This is the older
  * structure that contains 32-bit counts and does not calculate the number of packets captured
  * after filtering.
  *
  * (4.4BSD, FreeBSD, OpenBSD)  This is the original 32-bit command from the 4.4BSD basic command
  * set.  It lacks the bs_capt field of the newer structure.  Not all implementations use the newer
  * structure, and in that case this command is named BIOCGSTATS.  Under Linux Fast-STREAMS, this
  * command is implemented by both bpfdrv(4) and bpfmod(4).  bpfmod(4) will intercept the command on
  * its way from the driver and will add its own counts int the statistics.  It adds its drops to
  * those of the driver in bs_drop.
  */
noinline __unlikely int
bpf_biocgstatsold(queue_t *q, mblk_t *mp, struct xp *xp)
{
	struct bpf_stat_old *bs;
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(*bs), 0)))
		return (-ENOSR);

	bs = (typeof(bs)) db->b_rptr;
	bzero(bs, sizeof(*bs));
	bs->bs_recv = xp->xray.bpf.stats.bs_recv;
	bs->bs_drop = xp->xray.bpf.stats.bs_drop;
	return (0);
}

/** bpf_biocghdrcmplt: - process BIOCGHDRCMPLT ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (uint) Gets the status of the "header complete" flag.  Set to zero if the link
  * level source address should be filled in automatically by the interface output routine.  Set to
  * one of the link level source address will be written, as provided, to the wire.  This flag is
  * initialized to zero by default.
  *
  * (4.4BSD)  This is on of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, this command is not necesarily supported by bpfdrv(4).  Support also depends on
  * whether the driver supports sending packets at all.
  */
noinline __unlikely int
bpf_biocghdrcmplt(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
		return (-ENOSR);

	*(uint *) db->b_rptr = ((xp->xray.bpf.flags & BPF_HDRCMPLT) != 0);
	return (0);
}

/** bpf_biocshdrcmplt: - process BIOCSHDRCMPLT ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (uint) Sets the status of the "header complete" flag.  Set to zero if the link
  * level source address should be filled in automatically by the interface output routine.  Set to
  * one of the link level source address will be written, as provided, to the wire.  This flag is
  * initialized to zero by default.
  *
  * (4.4BSD)  This is on of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, this command is not necesarily supported by bpfdrv(4).  Support also depends on
  * whether the driver supports sending packets at all.
  */
noinline __unlikely int
bpf_biocshdrcmplt(struct xp *xp, mblk_t *dp)
{
	uint val = *(uint *) dp->b_rptr;

	if (val)
		xp->xray.bpf.flags |= BPF_HDRCMPLT;
	else
		xp->xray.bpf.flags &= ~BPF_HDRCMPLT;
	return (0);
}

/** bpf_biocgseesent: - process BIOCGSEESENT ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (uint)  This command is obsolete but left for compatibility.  Use BIOCGDIRECTION
  * instead.  Gets the flag determining whether locally generated packets on the interface should be
  * returned by bpf.  Set to zero to see only incoming packets on the interface.  Set to one to see
  * packets originating locally and remotely on the interface.  This flag is initialized to one by
  * default.
  *
  * (4.4BSD)  This is one of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, this command is implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocgseesent(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
		return (-ENOSR);

	*(uint *) db->b_rptr = ((xp->xray.bpf.flags & BPF_SEESENT) != 0);
	return (0);
}

/** bpf_biocsseesent: - process BIOCSSEESENT ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (uint)  This command is obsolete but left for compatibility.  Use BIOCSDIRECTION
  * instead.  Sets the flag determining whether locally generated packets on the interface should be
  * returned by bpf.  Set to zero to see only incoming packets on the interface.  Set to one to see
  * packets originating locally and remotely on the interface.  This flag is initialized to one by
  * default.
  *
  * (4.4BSD)  This is one of the original commands from the 4.4BSD implementation set.  Under Linux
  * Fast-STREAMS, this command is implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocsseesent(struct xp *xp, mblk_t *dp)
{
	uint val = *(uint *) dp->b_rptr;

	if (val) {
		xp->xray.bpf.flags |= BPF_SEESENT;
		xp->xray.bpf.direction = BPF_D_INOUT;
	} else {
		xp->xray.bpf.flags &= ~BPF_SEESENT;
		xp->xray.bpf.direction = BPF_D_IN;
	}
	return (0);
}

#ifdef BIOCGDIRECTION
/** bpf_biocgdirection: - process BIOCGDIRECTION ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (uint)  Get the setting determining whether incoming, outgoing, or all packets
  * on the interface should be returned by BPF.  Set to BPF_D_IN to see only incoming packets on the
  * interface.  Set to BPF_D_INOUT to see packets originating locally and remotely on the interface.
  * Set to BPF_D_OUT to see only outgoing packets on the interface.  This setting is intialized to
  * BPF_D_INOUT by default.
  *
  * (FreeBSD only.)  These are FreeBSD-only commands that further address the security problem
  * addressed by BIOCLOCK, by restricting the direciton of data.  Under Linux Fast-STREAMS, these
  * commands are implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocgdirection(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
		return (-ENOSR);

	*(uint *) db->b_rptr = xp->xray.bpf.direction;
	return (0);
}
#endif				/* BIOCGDIRECTION */

#ifdef BIOCSDIRECTION
/** bpf_biocsdirection: - process BIOCSDIRECTION ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (uint)  Set the setting determining whether incoming, outgoing, or all packets
  * on the interface should be returned by BPF.  Set to BPF_D_IN to see only incoming packets on the
  * interface.  Set to BPF_D_INOUT to see packets originating locally and remotely on the interface.
  * Set to BPF_D_OUT to see only outgoing packets on the interface.  This setting is intialized to
  * BPF_D_INOUT by default.
  *
  * (FreeBSD only.)  These are FreeBSD-only commands that further address the security problem
  * addressed by BIOCLOCK, by restricting the direciton of data.  Under Linux Fast-STREAMS, these
  * commands are implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocsdirection(struct xp *xp, mblk_t *dp)
{
	uint direction = *(uint *) dp->b_rptr;

	switch (direction) {
	case BPF_D_IN:
		xp->xray.bpf.flags &= ~BPF_SEESENT;
		break;
	case BPF_D_OUT:
		xp->xray.bpf.flags |= BPF_SEESENT;
		break;
	case BPF_D_INOUT:
		xp->xray.bpf.flags |= BPF_SEESENT;
		break;
	default:
		return (-EINVAL);
	}
	xp->xray.bpf.direction = direction;
}
#endif				/* BIOCSDIRECTION */

#ifdef BIOCGDIRFILT
/** bpf_biocgdirfilt: - process BIOCGDIRFILT ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (uint)  Get the status of the "direction filter" flag.  When non-zero, packets
  * matching the specified direction (either BPF_DIRECTION_IN or BPF_DIRECTION_OUT) will be ignored.
  *
  * (OpenBSD only.)  The rationale for these commands is that OpenBSD does not support the FreeBSD
  * BIOCSDIRECTION and BIOCGDIRECTION commands, so these commands perform a similar action.  Under
  * Linux Fast-STREAMS, these commands are implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocgdirfilt(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;
	uint dirfilt;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
		return (-ENOSR);

	switch (xp->xray.bpf.direction) {
	case BPF_D_OUT:
		dirfilt = BPF_DIRECTION_IN;
		break;
	case BPF_D_IN:
		dirfilt = BPF_DIRECTION_OUT;
		break;
	default:
		__swerr();
		/* fall through */
	case BPF_D_INOUT:
		dirfilt = BPF_DIRECTION_NONE;
		break;

	}
	*(uint *) db->b_rptr = dirfilt;
	return (0);
}
#endif				/* BIOCGDIRFILT */

#ifdef BIOCSDIRFILT
/** bpf_biocsdirfilt: - process BIOCSDIRFILT ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (uint)  Set the status of the "direction filter" flag.  When non-zero, packets
  * matching the specified direction (either BPF_DIRECTION_IN or BPF_DIRECTION_OUT) will be ignored.
  *
  * (OpenBSD only.)  The rationale for these commands is that OpenBSD does not support the FreeBSD
  * BIOCSDIRECTION and BIOCGDIRECTION commands, so these commands perform a similar action.  Under
  * Linux Fast-STREAMS, these commands are implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocsdirfilt(struct xp *xp, mblk_t *dp)
{
	uint dirfilt = *(uint *) dp->b_rptr;
	uint direction;

	switch (dirfilt) {
	case BPF_DIRECTION_IN:
		direction = BPF_D_OUT;
		xp->xray.bpf.flags |= BPF_SEESENT;
		break;
	case BPF_DIRECTION_OUT:
		direction = BPF_D_IN;
		xp->xray.bpf.flags &= ~BPF_SEESENT;
		break;
	case BPF_DIRECTION_NONE:
		direction = BPF_D_INOUT;
		xp->xray.bpf.flags |= BPF_SEESENT;
		break;
	default:
		return (-EINVAL);
	}
	xp->xray.bpf.direction = direction;
	return (0);
}
#endif				/* BIOCSDIRFILT */

#ifdef BIOCFEEDBACK
/** bpf_biocfeedback: - process BIOCFEEDBACK ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (uint)  Set packet feedback mode.  This allows injected packets to be fed back
  * as input to the interface when output via the interface is successful.  When BPF_D_INOUT
  * direction dis set, injected outgoing packet is not returned by BPF to avoid duplication.  This
  * flag is initialized to zero by default.
  *
  * (FreeBSD, NetBSD.)  This is the original FreeBSD command implemetned by NetBSD fr compatibility
  * with FreeBSD.  Under Linux Fast-STREAMS, this command is implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocfeedback(struct xp *xp, mblk_t *dp)
{
	uint val = *(uint *) dp->b_rptr;

	if (val)
		xp->xray.bpf.flags |= BPF_FEEDBACK;
	else
		xp->xray.bpf.flags &= ~BPF_FEEDBACK;
	return (0);
}
#endif				/* BIOCFEEDBACK */

/** bpf_biocgfeedback: - process BIOCGFEEDBACK ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (uint)  Get pacekt feedback mode.  This is the OpenBSD versions of the FreeBSD
  * command BIOCFEEDBACK, which cannot reset feedback mode once set.
  *
  * (OpenBSD, NetBSD.)  These are the FreeBSD workalikes that also permit examining the setting.
  * Under Linux Fast-STREAMS, these commands are implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocgfeedback(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(uint), 0)))
		return (-ENOSR);

	*(uint *) db->b_rptr = ((xp->xray.bpf.flags & BPF_FEEDBACK) != 0);
	return (0);
}

/** bpf_biocsfeedback: - process BIOCSFEEDBACK ioctl
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (uint)  Set packet feedback mode.  This is the OpenBSD versions of the FreeBSD
  * command BIOCFEEDBACK, which cannot reset feedback mode once set.
  *
  * (OpenBSD, NetBSD.)  These are the FreeBSD workalikes that also permit examining the setting.
  * Under Linux Fast-STREAMS, these commands are implemented by bpfdrv(4).
  */
noinline __unlikely int
bpf_biocsfeedback(struct xp *xp, mblk_t *dp)
{
	uint val = *(uint *) dp->b_rptr;

	if (val)
		xp->xray.bpf.flags |= BPF_FEEDBACK;
	else
		xp->xray.bpf.flags &= ~BPF_FEEDBACK;
	return (0);
}

/*
 *  SOCKIO input-output controls
 *  -------------------------------------------------------------------------
 */

#ifdef SIOCGIFNUM
/** sio_siocgifnum: - process SIOCGIFNUM ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  *
  * See bpfdrv(4).  (int)  Get the number of interfaces.  This request returns an integer that is
  * the number of interface descriptions (struct ifreq) that will be returned by the SIOCGIFCONF
  * ioctl; that is, it gives and indication of how large ifc_len has to be.
  *
  * The bpfdrv(4) must support this command so that the capture application is aware of the
  * necessary sizing of the buffer passed to SIOCGIFCONF.
  */
noinline __unlikely int
sio_siocgifnum(queue_t *q, mblk_t *mp, struct xp *xp)
{
	mblk_t *db;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	int num, card, span, chan;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(int), 0)) == NULL)
		return (-ENOSR);
	for (card = 0; card < X400_CARDS; card++) {
		if (!(cd = x400p_cards[card]))
			continue;
		/* one for the card */
		num++;
		for (span = 0; span < cd->ports; span++) {
			if (!(sp = cd->spans[span]))
				continue;
			/* one for the span */
			num++;
			for (chan = 0; chan < 32; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				/* one for the channel */
				num++;
			}
		}
	}
	*(int *) db->b_rptr = num;
	return (0);
}
#endif				/* SIOCGIFNUM */

/** sio_siocgifconf: - process SIOCGIFCONF ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct ifconf)  Returns a list of interface addresses.  This currently means
  * only addresses of the AF_INET (IPv4) family for compatibility.  The user passes an ifconf
  * structure as the (third) argument to the ioctl(2s) call.  It contains a pointer to an array of
  * ifreq structures in ifc_req and its length in bytes in ifc_len.  The driver fills the ifreq
  * structures with all current L3 interface addresses that are running: ifr_name contains the
  * interface addresses that are running: ifr_name contains the interface name (eth0:1, etc.),
  * ifr_addr the address.  The driver returns the actual length in ifc_len.  If ifc_len is equal to
  * the original length the buffer probably has overflowed and the caller should retry with a larger
  * buffer to get all addresses.  When no error occurs, the command returns zero (0); otherwise
  * minus one (1) and errno(3) is set to an appropriate error code.  Overflow is not considered an
  * error.
  *
  * This command should be supported by the driver to permit the capture application to obtain a
  * list of valid interface names for use with the BIOCSETIF command.  The addresses returned in
  * ifr_addr are not important, and could simply be 0.0.0.0.
  */
noinline __unlikely int
sio_siocgifconf(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct ifconf *ifc = (typeof(ifc)) dp->b_rptr;
	int max = ifc->ifc_len / sizeof(struct ifreq);
	caddr_t uaddr = (caddr_t) ifc->ifc_buf;
	struct ifreq *ifr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	int card, num, span, chan;
	mblk_t *db;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*ifc), 0)) == NULL)
		return (-ENOSR);

	ifc = (typeof(ifc)) db->b_rptr;
	bcopy(ifc, db->b_rptr, sizeof(*ifc));

	if ((db = mi_copyout_alloc(q, mp, uaddr, ifc->ifc_len, 0)) == NULL)
		return (-ENOSR);

	ifr = (typeof(ifr)) db->b_rptr;
	bzero(ifr, sizeof(*ifr));

	for (num = 0, card = 0; card < X400_CARDS && num < max; card++) {
		if (!(cd = x400p_cards[card]))
			continue;
		/* one for the card */
		snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d", card);
		ifr->ifr_addr.sa_family = AF_UNSPEC;
		ifr++;
		num++;
		for (span = 0; span < cd->ports && num < max; span++) {
			if (!(sp = cd->spans[span]))
				continue;
			/* one for the span */
			snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d.%d", card, span);
			ifr->ifr_addr.sa_family = AF_UNSPEC;
			ifr++;
			num++;
			for (chan = 0; chan < 32 && num < max; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				/* one for the channel */
				snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d.%d:%d", card, span, chan);
				ifr->ifr_addr.sa_family = AF_UNSPEC;
				ifr++;
				num++;
			}
		}
	}
	ifc->ifc_len = num * sizeof(*ifr);
	return (0);
}

#ifdef __LP64__
/** sio_siocgifconf32: - process 32-bit SIOCGIFCONF ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  */
noinline __unlikely int
sio_siocgifconf32(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct ifconf32 *ifc = (typeof(ifc)) dp->b_rptr;
	int max = ifc->ifc_len / sizeof(struct ifreq);
	caddr_t uaddr = (caddr_t)(ulong) ifc->ifc_buf;
	struct ifreq *ifr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	int card, num, span, chan;
	mblk_t *db;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*ifc), 0)) == NULL)
		return (-ENOSR);

	ifc = (typeof(ifc)) db->b_rptr;
	bcopy(ifc, db->b_rptr, sizeof(*ifc));

	if ((db = mi_copyout_alloc(q, mp, uaddr, ifc->ifc_len, 0)) == NULL)
		return (-ENOSR);

	ifr = (typeof(ifr)) db->b_rptr;
	bzero(ifr, sizeof(*ifr));

	for (num = 0, card = 0; card < X400_CARDS && num < max; card++) {
		if (!(cd = x400p_cards[card]))
			continue;
		/* one for the card */
		snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d", card);
		ifr->ifr_addr.sa_family = AF_UNSPEC;
		ifr++;
		num++;
		for (span = 0; span < cd->ports && num < max; span++) {
			if (!(sp = cd->spans[span]))
				continue;
			/* one for the span */
			snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d.%d", card, span);
			ifr->ifr_addr.sa_family = AF_UNSPEC;
			ifr++;
			num++;
			for (chan = 0; chan < 32 && num < max; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				/* one for the channel */
				snprintf(ifr->ifr_name, IFNAMSIZ, DRV_NAME "%d.%d:%d", card, span, chan);
				ifr->ifr_addr.sa_family = AF_UNSPEC;
				ifr++;
				num++;
			}
		}
	}
	ifc->ifc_len = num * sizeof(*ifr);
	return (0);
}
#endif				/* __LP64__ */

noinline fastcall __unlikely struct cd *
find_ifname_cd(queue_t *q, mblk_t *mp, const char *name)
{
	/* FIXME */
	return (NULL);
}

noinline fastcall __unlikely struct sp *
find_ifname_sp(queue_t *q, mblk_t *mp, const char *name)
{
	/* FIXME */
	return (NULL);
}

noinline fastcall __unlikely struct ch *
find_ifname_ch(queue_t *q, mblk_t *mp, const char *name)
{
	/* FIXME */
	return (NULL);
}

/** sio_siocgifflags: - process SIOCGIFFLAGS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct ifreq)  Get the active flag word of the device
  * specified by ifr_name.  ifr_flags contains a bit mask of the following
  * values:
  *
  *     IFF_UP		    interface is running.
  *     IFF_BROADCAST	    valid broadcast address set.
  *     IFF_DEBUG	    internal debugging flag.
  *     IFF_LOOPBACK	    interface is loopback interface.
  *     IFF_POINTTOPOINT    interface is point-to-point link.
  *     IFF_RUNNING	    interface has resources allocated.
  *     IFF_NOARP	    no arm, L2 dest. address unset.
  *     IFF_PROMISC	    interface in promiscuous mode.
  *     IFF_NOTRAILERS	    avoid use of trailers.
  *     IFF_ALLMULTI	    receive all multicast packets.
  *
  * Seting the active flag word is a privileged operation, but any process may read it.
  *
  * The bpfdrv(4) must support the SIOCGIFFLAGS command and the IFF_UP flag.  libpcap checks flags
  * with the SIOCGIFFLAGS command and skips all interfaces without the IFF_UP flag set.  The command
  * can return [ENXIO] which will also cause libpcap to skip the interface.  Any other error will
  * cause libpcap to bork out.
  */
noinline __unlikely int
sio_siocgifflags(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct ifreq *ifr = (typeof(ifr)) dp->b_rptr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	mblk_t *db;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(*ifr), 0)))
		return (-ENOSR);

	bcopy(ifr, db->b_rptr, sizeof(*ifr));
	ifr = (typeof(ifr)) db->b_rptr;

	ifr->ifr_flags = 0;
	ifr->ifr_flags |= IFF_POINTTOPOINT;
	ifr->ifr_flags |= IFF_NOARP;

	if ((cd = find_ifname_cd(q, mp, ifr->ifr_name))) {
		ifr->ifr_flags |= IFF_UP;
		ifr->ifr_flags |= IFF_RUNNING;
		ifr->ifr_flags |= IFF_NOXMIT;
		ifr->ifr_flags |= IFF_NOLOCAL;
		// ifr->ifr_ppa = (card << 12);
		return (0);
	}
	if ((sp = find_ifname_sp(q, mp, ifr->ifr_name))) {
		if (sp->config.ifflags & SDL_IF_UP)
			ifr->ifr_flags |= IFF_UP;
		if (sp->config.ifflags & (SDL_IF_RX_RUNNING | SDL_IF_TX_RUNNING))
			ifr->ifr_flags |= IFF_RUNNING;
		// ifr->ifr_ppa = (sp->cd->card << 12) | (sp->span << 8);
		return (0);
	}
	if ((ch = find_ifname_ch(q, mp, ifr->ifr_name))) {
		if (ch->sdl.config.ifflags & SDL_IF_UP)
			ifr->ifr_flags |= IFF_UP;
		if (ch->sdl.config.ifflags & (SDL_IF_RX_RUNNING | SDL_IF_TX_RUNNING))
			ifr->ifr_flags |= IFF_RUNNING;
		// ifr->ifr_ppa = ch->ppa;
		return (0);
	}
	return (-ENXIO);
}

/** sio_siocsifflags: - process SIOCSIFFLAGS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct ifreq)  Set the active flag word of the device specified by ifr_name.
  * ifr_flags contains a bit mask of the following values:
  *
  *     IFF_UP		    interface is running.
  *     IFF_BROADCAST	    valid broadcast address set.
  *     IFF_DEBUG	    internal debugging flag.
  *     IFF_LOOPBACK	    interface is loopback interface.
  *     IFF_POINTTOPOINT    interface is point-to-point link.
  *     IFF_RUNNING	    interface has resources allocated.
  *     IFF_NOARP	    no arm, L2 dest. address unset.
  *     IFF_PROMISC	    interface in promiscuous mode.
  *     IFF_NOTRAILERS	    avoid use of trailers.
  *     IFF_ALLMULTI	    receive all multicast packets.
  *
  * Seting the active flag word is a privileged operation, but any process may read it.
  *
  * The bpfdrv(4) must support the SIOCGIFFLAGS command and the IFF_UP flag.  libpcap checks flags
  * with the SIOCGIFFLAGS command and skips all interfaces without the IFF_UP flag set.  The command
  * can return [ENXIO] which will also cause libpcap to skip the interface.  Any other error will
  * cause libpcap to bork out.
  *
  * Note that the SIOCSIFFLAGS input-output control command also sets the PPA.
  */
noinline __unlikely int
sio_siocsifflags(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct ifreq *ifr = (typeof(ifr)) dp->b_rptr;
	struct ch *ch = find_ifname_ch(q, mp, ifr->ifr_name);

	if (ch == NULL)
		return (-ENXIO);
	/* FIXME */
	return (-EOPNOTSUPP);
}

noinline __unlikely int
sio_siocgifindex(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}

#ifdef SIOCSIFINDEX
noinline __unlikely int
sio_siocsifindex(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCSIFINDEX */

noinline __unlikely int
sio_siocgifhwaddr(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}

noinline __unlikely int
sio_siocsifhwaddr(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}

#ifdef SIOCGLIFNUM
/** sio_siocglifnum: - process SIOCGLIFNUM ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct lifnum)  Get the number of interfaces.  This request returns an integer
  * that is the the number of interface descriptions (struct lifreq) that will be returned by the
  * SIOCGLIFCONF ioctl; that is, it gives an indication of how large lifc_len has to be.  This
  * request takes an lifnum structure as a value-result parameter.  The lifn_family field should be
  * set to AF_UNSPEC to count both AF_INET and AF_INET6 interfaces.  The lifn_flags field should be
  * initially set to zero.
  *
  * The bpfdrv(4) must support this command so that the capture application is aware of the
  * necessary sizing of the buffer passed to SIOCGLIFCONF.
  */
noinline __unlikely int
sio_siocglifnum(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct lifnum *lifn = (typeof(lifn)) dp->b_rptr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	int card, span, chan;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*lifn), 0)) == NULL)
		return (-ENOSR);

	lifn = (typeof(lifn)) db->b_rptr;
	bcopy(lifn, db->b_rptr, sizeof(*lifn));

	if (lifn->lifn_family != AF_UNSPEC) {
		lifn->lifn_count = 0;
		return (0);
	}
	lifn->lifn_count = 0;

	for (card = 0; card < X400_CARDS; card++) {
		if (!(cd = x400p_cards[card]))
			continue;
		lifn->lifn_count++;	/* one for the card */
		for (span = 0; span < cd->ports; span++) {
			if (!(sp = cd->spans[span]))
				continue;
			if (!(sp->config.ifflags & SDL_IF_UP)
			    && (lifn->lifn_flags & (LIFC_ENABLED | LIFC_EXTERNAL_SOURCE)))
				continue;
			lifn->lifn_count++;	/* one for the span */
			for (chan = 0; chan < 32; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				if (!ch->xp && (lifn->lifn_flags & (LIFC_NOXMIT | LIFC_EXTERNAL_SOURCE)))
					/* no transmit, no external source */
					continue;
				if (!(ch->sdl.config.ifflags & SDL_IF_UP)
				    && (lifn->lifn_flags & (LIFC_ENABLED | LIFC_EXTERNAL_SOURCE)))
					/* no enabled, no external source */
					continue;
				/* one for the channel */
				lifn->lifn_count++;
			}
		}
	}
	return (0);
}
#endif				/* SIOCGLIFNUM */

#ifdef SIOCGLIFCONF
/** sio_siocglifconf: - process SIOCGLIFCONF ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct lifconf)  Get interface configuration list.  This request takes an
  * lifconf structure as a value-result parameter.  The lic_family field can be set to AF_UNSPEC to
  * retrieve both AF_INET and AF_INET6 interfaces.  The lifc_flags field should be set to zero.  Te
  * lifc_len field should be set to the size of the buffer pointed to by lifc_buf.  Upon success,
  * lifc_len will contain the length, in bytes, of the array of lifreq structures pointed to by
  * lifc_req.  For eaach lifcreq structure, the lifr_name and lifr_addr fields will be valid.
  */
noinline __unlikely int
sio_siocglifconf(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct lifconf *lifc = (typeof(lifc)) dp->b_rptr;
	int max = lifc->lifc_len / sizeof(struct lifreq);
	caddr_t uaddr = (caddr_t) lifc->lifc_buf;
	struct lifreq *lifr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	int card, num, span, chan;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*lifc), 0)) == NULL)
		return (-ENOSR);

	lifc = (typeof(*lifc)) db->b_rptr;
	bcopy(lifc, db->b_rptr, sizeof(*lifc));

	if ((db = mi_copyout_alloc(q, mp, uaddr, lifc->lifc_len, 0)) == NULL)
		return (-ENOSR);

	lifr = (typeof(lifr)) db->b_rptr;
	if (lifc->lifc_family != AF_UNSPEC) {
		lifc->lifc_len = 0;
		return (0);
	}
	lifr->lifr_len = 0;
	for (num = 0, card = 0; card < X400_CARDS && num < max; card++) {
		if (!(cd = x400p_cards[card]))
			continue;
		/* one for the card */
		snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d", card);
		lifr->lifr_addr.sa_familty = AF_UNSPEC;
		lifr++;
		num++;
		for (span = 0; span < cd->ports && num < max; span++) {
			if (!(sp = cd->spans[span]))
				continue;
			if (!(sp->config.ifflags & SDL_IF_UP)
			    && (lifc->lifc_flags & (LIFC_ENABLED | LIFC_EXTERNAL_SOURCE)))
				continue;
			/* one for the span */
			snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d.%d", card, span);
			lifr->lifr_addr.sa_family = AF_UNSPEC;
			lifr++;
			num++;
			for (chan = 0; chan < 32; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				if (!ch->xp && (lifc->lifc_flags & (LIFC_NOXMIT | LIFC_EXTERNAL_SOURCE)))
					continue;
				if (!(ch->sdl.config.ifflags & SDL_IF_UP)
				    && (lifc->lifc_flags & (LIFC_ENABLED | LIFC_EXTERNAL_SOURCE)))
					continue;
				snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d.%d:%d", card, span,
					 ch->chan);
				lifr->lifr_addr.sa_family = AF_UNSPEC;
				lifr++;
				num++;
			}

		}
	}
	lifr->lifr_len = num * sizeof(*lifr);
	return (0);
}

#ifdef __LP64__
/** sio_siocglifconf32: - process 32-bit SIOCGLIFCONF ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  */
noinline __unlikely int
sio_siocglifconf32(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct lifconf32 *lifc = (typeof(lifc)) dp->b_rptr;
	int max = lifc->lifc_len / sizeof(struct lifreq);
	caddr_t uaddr = (caddr_t) lifc->lifc_buf;
	struct lifreq *lifr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	int card, num, span, chan;

	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*lifc), 0)) == NULL)
		return (-ENOSR);

	lifc = (typeof(*lifc)) db->b_rptr;
	bcopy(lifc, db->b_rptr, sizeof(*lifc));

	if ((db = mi_copyout_alloc(q, mp, uaddr, lifc->lifc_len, 0)) == NULL)
		return (-ENOSR);

	lifr = (typeof(lifr)) db->b_rptr;
	if (lifc->lifc_family != AF_UNSPEC) {
		lifc->lifc_len = 0;
		return (0);
	}
	lifr->lifr_len = 0;
	for (num = 0, card = 0; card < X400_CARDS && num < max; card++) {
		if (!(cd = x400p_cards[card]))
			continue;
		/* one for the card */
		snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d", card);
		lifr->lifr_addr.sa_familty = AF_UNSPEC;
		lifr++;
		num++;
		for (span = 0; span < cd->ports && num < max; span++) {
			if (!(sp = cd->spans[span]))
				continue;
			if (!(sp->config.ifflags & SDL_IF_UP)
			    && (lifc->lifc_flags & (LIFC_ENABLED | LIFC_EXTERNAL_SOURCE)))
				continue;
			/* one for the span */
			snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d.%d", card, span);
			lifr->lifr_addr.sa_family = AF_UNSPEC;
			lifr++;
			num++;
			for (chan = 0; chan < 32; chan++) {
				if (!(ch = sp->chans[chan]))
					continue;
				if (!ch->xp && (lifc->lifc_flags & (LIFC_NOXMIT | LIFC_EXTERNAL_SOURCE)))
					continue;
				if (!(ch->sdl.config.ifflags & SDL_IF_UP)
				    && (lifc->lifc_flags & (LIFC_ENABLED | LIFC_EXTERNAL_SOURCE)))
					continue;
				snprintf(lifr->lifr_name, LIFNAMSIZ, DRV_NAME "%d.%d:%d", card, span,
					 ch->chan);
				lifr->lifr_addr.sa_family = AF_UNSPEC;
				lifr++;
				num++;
			}

		}
	}
	lifr->lifr_len = num * sizeof(*lifr);
	return (0);
}
#endif				/* __LP64__ */
#endif				/* SIOCGLIFCONF */

#ifdef SIOCSLIFNAME
/** sio_siocslifname: - process SIOCSLIFNAME ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  *     Note that the SIOCSLIFNAME input-output control command also sets the PPA.
  */
noinline __unlikely int
sio_siocslifname(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct lifreq *lifr = (typeof(lifr)) dp->b_rptr;

	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCSLIFNAME */

#ifdef SIOCGLIFFLAGS
/** sio_siocglifflags: - process SIOCGLIFFFLAGS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct lifreq)  Get the active flag word of the device specified by lifr_name.
  * lifr_flags contains a bit mask of the same values as SIOCGIFFLAGS.
  *
  * The bpfdrv(4) must support the SIOCGLIFFLAGS command and the IFF_UP flag.  libpcap checks flags
  * with the SIOCGLIFFLAGS command and skips all interfaces without the IFF_UP flag set.  The
  * command can return [ENXIO] which will also cause libpcap to skip the interface.  Any other error
  * will cause libpcap to bork out.
  */
noinline __unlikely int
sio_siocglifflags(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct lifreq *lifr = (typeof(lifr)) dp->b_rptr;
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;

	if (!(db = mi_copyout_alloc(q, mp, 0, sizeof(*lifr), 0)))
		return (-ENOSR);

	bcopy(lifr, db->b_rptr, sizeof(*lifr));
	lifr = (typeof(lifr)) db->b_rptr;

	lifr->lifr_flags = 0;
	lifr->lifr_flags |= IFF_POINTTOPOINT;
	lifr->lifr_flags |= IFF_NOARP;

	if ((cd = find_ifname_cd(q, mp, lifr->lifr_name))) {
		lifr->lifr_flags |= IFF_UP;
		lifr->lifr_flags |= IFF_RUNNING;
		lifr->lifr_flags |= IFF_NOXMIT;
		lifr->lifr_flags |= IFF_NOLOCAL;
		lifr->lifr_ppa = (card << 12);
		return (0);
	}
	if ((sp = find_ifname_sp(q, mp, lifr->lifr_name))) {
		if (sp->config.ifflags & SDL_IF_UP)
			lifr->lifr_flags |= IFF_UP;
		if (sp->config.ifflags & (SDL_IF_RX_RUNNING | SDL_IF_TX_RUNNING))
			lifr->lifr_flags |= IFF_RUNNING;
		lifr->lifr_ppa = (sp->cd->card << 12) | (sp->span << 8);
		return (0);
	}
	if ((ch = find_ifname_ch(q, mp, lifr->lifr_name))) {
		if (ch->sdl.config.ifflags & SDL_IF_UP)
			lifr->lifr_flags |= IFF_UP;
		if (ch->sdl.config.ifflags & (SDL_IF_RX_RUNNING | SDL_IF_TX_RUNNING))
			lifr->lifr_flags |= IFF_RUNNING;
		lifr->lifr_ppa = ch->ppa;
		return (0);
	}
	return (-ENXIO);
}
#endif				/* SIOCGLIFFLAGS */

#ifdef SIOCSLIFFLAGS
/** sio_siocslifflags: - process SIOCSLIFFFLAGS ioctl
  * @q: write queue
  * @mp: M_IOCTL/M_IOCDATA message
  * @xp: private structure
  * @dp: M_IOCTL/M_IOCDATA data block
  *
  * See bpfdrv(4).  (struct lifreq)  Set the active flag word of the device specified by lifr_name.
  * lifr_flags contains a bit mask of the same values as SIOCGIFFLAGS.
  *
  * The bpfdrv(4) must support the SIOCGLIFFLAGS command and the IFF_UP flag.  libpcap checks flags
  * with the SIOCGLIFFLAGS command and skips all interfaces without the IFF_UP flag set.  The
  * command can return [ENXIO] which will also cause libpcap to skip the interface.  Any other error
  * will cause libpcap to bork out.
  *
  * Note that the SIOCSLIFNAME input-output control command could also be used to set the PPA.
  */
noinline __unlikely int
sio_siocslifflags(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	struct lifreq *lifr = (typeof(lifr)) dp->b_rptr;
	struct ch *ch = find_ifname_ch(q, mp, lifr->lifr_name);

	if (ch == NULL)
		return (-ENXIO);
	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCSLIFFLAGS */

#ifdef SIOCGLIFINDEX
noinline __unlikely int
sio_siocglifindex(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCGLIFINDEX */

#ifdef SIOCSLIFINDEX
noinline __unlikely int
sio_siocslifindex(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCSLIFINDEX */

#ifdef SIOCGLIFHWADDR
noinline __unlikely int
sio_siocglifhwaddr(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCGLIFHWADDR */

#ifdef SIOCSLIFHWADDR
noinline __unlikely int
sio_siocslifhwaddr(queue_t *q, mblk_t *mp, struct xp *xp, mblk_t *dp)
{
	/* FIXME */
	return (-EOPNOTSUPP);
}
#endif				/* SIOCSLIFHWADDR */

/*
 *  DLIOC input-output controls
 *  -------------------------------------------------------------------------
 */

#ifdef DLIOCRAW
noinline __unlikely int
dl_dliocraw(queue_t *q, mblk_t *mp, struct xp *xp)
{
	/* DLIOCRAW: see dlpi_ioctl(4).  Just uses M_DATA instead of DL_UNITDATA_IND and DL_UNITDATA_REQ. */
	/* FIXME: set raw mode */
	return (-EINVAL);
}
#endif				/* DLIOCRAW */

#ifdef DLIOCNATIVE
noinline __unlikely int
dl_dliocnative(queue_t *q, mblk_t *mp, struct xp *xp)
{
	/* DLIOCNATIVE: see dlpi_ioctl(4).  Causes a native dl_mac_type instead of the default one. So,
	   DL_HDLC instead of DL_OTHER (for signalling link). */
	/* FIXME: set native mode */
	mi_copy_set_rval(mp, DL_HDLC);
	return (0);
}
#endif				/* DLIOCNATIVE */

#ifdef DLIOCMARGININFO
noinline __unlikely int
dl_dliocmargininfo(queue_t *q, mblk_t *mp, struct xp *xp)
{
	int *arg;
	struct ch *ch;
	mblk_t *db;

	if ((ch = xp->ch) == NULL)
		return (-EINVAL);
	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*arg), 0))) {
		/* The margin is the amount of room available for a native header.  So for SS7 this is the L2 
		   header size, which is different depending on whether it is for Annex A or not. */
		arg = (typeof(arg)) db->b_rptr;
		bzero(arg, sizeof(*arg));
		*arg = (ch->option.popt & SS7_POPT_XSN) ? 6 : 3;
		return (0);
	}
	return (-ENOSR);
}
#endif				/* DLIOCMARGININFO */

#ifdef DLIOCHDRINFO
noinline __unlikely int
dl_dliochdrinfo(struct xp *xp, mblk_t *dp)
{
	dl_unitdata_req_t *arg = (typeof(arg)) dp->b_rptr;

	if (arg->dl_primitive != DL_UNITDATA_REQ)
		return (-EINVAL);
	return (-EINVAL);	/* not supported */
}
#endif				/* DLIOCHDRINFO */

#ifdef DL_IOC_DRIVER_OPTIONS
noinline __unlikely int
dl_ioc_driver_options(struct xp *xp, mblk_t *dp)
{
	driver_ops_t *arg = (typeof(arg)) dp->b_rptr;

	if (arg->driver_ops_type != 0)
		return (-EOPNOTSUPP);
	if (arg->driver_ops_type_1 != 0)
		return (-EOPNOTSUPP);
	if (arg->driver_ops_type_2 != 0)
		return (-EOPNOTSUPP);
	/* otherwise ok */
	return (0);
}
#endif				/* DL_IOC_DRIVER_OPTIONS */

#ifndef DLIOCHDRINFO
#ifdef DL_IOC_HDR_INFO
noinline __unlikely int
dl_ioc_hdr_info(struct xp *xp, mblk_t *dp)
{
	dl_unitdata_req_t *arg = (typeof(arg)) dp->b_rptr;

	if (arg->dl_primitive != DL_UNITDATA_REQ)
		return (-EINVAL);
	return (-EINVAL);	/* not supported */
}
#endif				/* DL_IOC_HDR_INFO */
#endif				/* DLIOCHDRINFO */

#ifdef DL_HP_SET_DRV_PARAM_IOCTL
noinline __unlikely int
dl_hp_set_drv_param_ioctl(struct xp *xp, mblk_t *dp)
{
	dl_hp_set_drv_param_ioctl_t *arg = (typeof(arg)) dp->b_rptr;
	struct ch *ch;

	if ((ch = xp->ch) == NULL)
		goto einval;
	if ((arg->dl_request & DL_HP_DRV_SPEED) && (arg->dl_speed != 0))
		goto einval;
	if ((arg->dl_request & DL_HP_DRV_DUPLEX) && (arg->dl_duplex != DL_HP_FULL_DUPLEX))
		goto einval;
	if ((arg->dl_request & DL_HP_DRV_AUTONEG)
	    && (arg->dl_autoneg != DL_HP_AUTONEG_SENSE_ON && arg->dl_autoneg != DL_HP_AUTONEG_SENSE_OFF))
		goto einval;
	if ((arg->dl_request & DL_HP_DRV_MTU) && (arg->dl_mtu != ch->sdt.config.m))
		goto einval;
	if (arg->dl_request & DL_HP_DRV_VALUE1)
		goto einval;
	if (arg->dl_request & DL_HP_DRV_VALUE2)
		goto einval;
	if (arg->dl_request & DL_HP_DRV_VALUE3)
		goto einval;
	if (arg->dl_request & DL_HP_DRV_RESERVED1)
		goto einval;
	if (arg->dl_request & DL_HP_DRV_RESERVED2)
		goto einval;
	if (arg->dl_request & DL_HP_DRV_RESERVED3)
		goto einval;
	if (arg->dl_request & DL_HP_DRV_RESERVED4)
		goto einval;
	/* otherwise ok */
	if (arg->dl_request & DL_HP_DRV_AUTONEG) {
		switch (arg->dl_autoneg) {
		case DL_HP_AUTONEG_SENSE_ON:
			ch->sdl.config.ifflags |= SDL_IF_AUTOCONFIG;
			break;
		case DL_HP_AUTONEG_SENSE_OFF:
			ch->sdl.config.ifflags &= ~SDL_IF_AUTOCONFIG;
			break;
		}
	}
	return (0);
      einval:
	return (-EINVAL);
}
#endif				/* DL_HP_SET_DRV_PARAM_IOCTL */

#ifdef DL_HP_GET_DRV_PARAM_IOCTL
noinline __unlikely int
dl_hp_get_drv_param_ioctl(queue_t *q, mblk_t *mp, struct xp *xp)
{
	dl_hp_set_drv_param_ioctl_t *arg;
	struct ch *ch;
	mblk_t *db;

	if ((ch = xp->ch) == NULL)
		return (-EINVAL);
	if ((db = mi_copyout_alloc(q, mp, 0, sizeof(*arg), 0))) {
		arg = (typeof(arg)) db->b_rptr;
		bzero(arg, sizeof(*arg));
		arg->dl_request = 0;
		arg->dl_speed = 0;
		arg->dl_request |= DL_HP_DRV_SPEED;
		arg->dl_duplex = DL_HP_FULL_DUPLEX;
		arg->dl_request |= DL_HP_DRV_DUPLEX;
		if (ch->sdl.config.ifflags & SDL_IF_AUTOCONFIG)
			arg->dl_autoneg = DL_HP_AUTONEG_SENSE_ON;
		else
			arg->dl_autoneg = DL_HP_AUTONEG_SENSE_OFF;
		arg->dl_request |= DL_HP_DRV_AUTONEG;
		arg->dl_mtu = ch->sdt.config.m;
		arg->dl_request |= DL_HP_DRV_MTU;
#if 0
		arg->dl_value1 = 0;
		arg->dl_request |= DL_HP_DRV_VALUE1;
		arg->dl_value2 = 0;
		arg->dl_request |= DL_HP_DRV_VALUE2;
		arg->dl_value3 = 0;
		arg->dl_request |= DL_HP_DRV_VALUE3;
		arg->dl_reserved1[0] = 0;
		arg->dl_request |= DL_HP_DRV_RESERVED1;
		arg->dl_reserved1[1] = 0;
		arg->dl_request |= DL_HP_DRV_RESERVED2;
		arg->dl_reserved2[0] = 0;
		arg->dl_request |= DL_HP_DRV_RESERVED3;
		arg->dl_reserved2[1] = 0;
		arg->dl_request |= DL_HP_DRV_RESERVED4;
#endif
		return (0);
	}
	return (-ENOSR);
}
#endif				/* DL_HP_GET_DRV_PARAM_IOCTL */

#ifdef DLPI_SET_NO_LOOPBACK
noinline __unlikely int
dl_set_no_loopback(struct xp *xp, mblk_t *dp)
{
	switch (*(uint32_t *) dp->b_rptr) {
	case 0:
	case 1:
		return (0);
	default:
		return (-EINVAL);
	}
}
#endif				/* DLPI_SET_NO_LOOPBACK */

/*
 *  =========================================================================
 *
 *  X400P Interrupt Service Routine
 *
 *  =========================================================================
 *  We break this out into E1 and T1 versions for speed.  No need to check card type in the ISR when
 *  it is static for the board.  That is: boards do not change type from E1 to T1 or visa versa.
 */

/** xp_span_process: - process E1/T1/J1 span
  * @sp: span structure
  * @wspan: beg tx block pointer
  * @rspan: beg rx block pointer
  * @wend:  end tx block pointer
  * @rend:  end rx block pointer
  *
  * Process an entire E1/T1/J1 span.  Process first as a High-Speed Link where all channels are
  * concatenated to form a signle link (chan == 0); then process the E1/T1/J1 channelized, one
  * channel at a time (chan != 0).
  */
noinline fastcall __hot void
xp_span_process(struct sp *sp, uchar *wspan, uchar *rspan, uchar *wend, uchar *rend)
{
	const int chanmax = (sp->config.ifgtype == SDL_GTYPE_E1) ? 31 : 24;
	int chan;

	for (chan = 0; chan <= chanmax; chan++) {
		struct ch *ch;

		if (likely((ch = sp->chans[chan]) != NULL)) {
			size_t coff;
			sdl_ulong ifflags;
			sdl_ulong iftype;

			prefetch(ch);
			coff = ((sp->config.ifgtype == SDL_GTYPE_E1)
				? xp_e1_chan_map[chan]
				: xp_t1_chan_map[chan]) << 2;
			ifflags = ch->sdl.config.ifflags;
			iftype = ch->sdl.config.iftype;

			if (ifflags & SDL_IF_UP) {
				sdt_stats_t *stats = &ch->sdt.stats;

				prefetchw(stats);
				if (ifflags & SDL_IF_TX_RUNNING)
					xp_tx_block(ch, wspan + coff, wend, stats, iftype);
				else
					xp_tx_idle(wspan + coff, wend, iftype);
				if (ifflags & SDL_IF_RX_RUNNING)
					xp_rx_block(ch, rspan + coff, rend, stats, iftype);
			} else
				xp_tx_idle(wspan + coff, wend, iftype);
		}
	}
}

/** xp_card_tasklet: - process E1/T1/J1 card
  * @data: span structure (opaque)
  *
  * Process an entire E1/T1/J1 card as a tasklet.  Each span is processed in order.  Only one tx and
  * one rx elastic buffer block (8 frames for 4 spans) is processed at a time to avoid latency
  * issues.
  *
  * This tasklet is scheduled before the ISR returns to feed the next buffer of data into the write
  * buffer and read the buffer of data from the read buffer.  This will run the soft-HDLC on each
  * channel for 8 more bytes, or if full span will run the soft-HDLC for 192 bytes (T1) or 256 bytes
  * (E1).
  */
STATIC __hot void
xp_card_tasklet(unsigned long data)
{
	struct cd *cd = (struct cd *) data;
	int uebno, reschedule = 0;
	psw_t flags;

	spin_lock_irqsave(&cd->lock, flags);
	if (likely((uebno = cd->uebno) != cd->lebno)) {
		if ((cd->uebno = (uebno + 1) & (X400P_EBUFNO - 1)) != cd->lebno)
			reschedule = 1;
		spin_unlock_irqrestore(&cd->lock, flags);
		{
			size_t boff = uebno << 10;
			uchar *wbeg = (uchar *) cd->wbuf + boff;
			uchar *wend = wbeg + 1024;
			uchar *rbeg = (uchar *) cd->rbuf + boff;
			uchar *rend = rbeg + 1024;
			int span;

			for (span = 0; span < cd->ports; span++) {
				struct sp *sp;

				if ((sp = cd->spans[span]) && (sp->config.ifflags & SDL_IF_UP)) {
					int soff = span_to_byte(span);

					xp_span_process(sp, wbeg + soff, rbeg + soff, wend, rend);
				}
			}
		}
		if (reschedule)
			tasklet_hi_schedule(&cd->tasklet);
	} else
		spin_unlock_irqrestore(&cd->lock, flags);
}

/** xp_overflow:- X400P Overflow
  * @cd: card structure
  *
  * I know that this is rather like kicking them when they are down, we are doing stats in the ISR
  * when takslets don't have enough time to run, but we are already in dire trouble if this is
  * happening anyway.  It should not take too much time to peg these counts.
  */
noinline fastcall void
xp_overflow(struct cd *cd)
{
	int span;

	_printd(("%s: card %d elastic buffer overrun!\n", __FUNCTION__, cd->card));
	for (span = 0; span < cd->ports; span++) {
		struct sp *sp;

		if ((sp = cd->spans[span]) && (sp->config.ifflags & SDL_IF_UP)) {
			switch (sp->config.iftype) {
				struct ch *ch;

			case SDL_TYPE_DS0:
			case SDL_TYPE_DS0A:
			{
				int chan;

				for (chan = 1; chan < 32; chan++) {
					if ((ch = sp->chans[chan])
					    && (ch->sdl.config.ifflags & SDL_IF_UP)) {
						if (ch->sdl.config.ifflags & SDL_IF_TX_RUNNING) {
							ch->sdl.stats.tx_underruns += 8;
							ch->sdt.stats.tx_underruns += 8;
						}
						if (ch->sdl.config.ifflags & SDL_IF_RX_RUNNING) {
							ch->sdl.stats.rx_overruns += 8;
							ch->sdt.stats.rx_overruns += 8;
						}
					}
				}
				break;
			}
			case SDL_TYPE_T1:
			case SDL_TYPE_J1:
				if ((ch = sp->chans[0]) && (ch->sdl.config.ifflags & SDL_IF_UP)) {
					if (ch->sdl.config.ifflags & SDL_IF_TX_RUNNING) {
						ch->sdl.stats.tx_underruns += 8 * 24;
						ch->sdt.stats.tx_underruns += 8 * 24;
					}
					if (ch->sdl.config.ifflags & SDL_IF_RX_RUNNING) {
						ch->sdl.stats.rx_overruns += 8 * 24;
						ch->sdt.stats.rx_overruns += 8 * 24;
					}
				}
				break;
			case SDL_TYPE_E1:
				if ((ch = sp->chans[0]) && (ch->sdl.config.ifflags & SDL_IF_UP)) {
					if (ch->sdl.config.ifflags & SDL_IF_TX_RUNNING) {
						ch->sdl.stats.tx_underruns += 8 * 31;
						ch->sdt.stats.tx_underruns += 8 * 31;
					}
					if (ch->sdl.config.ifflags & SDL_IF_RX_RUNNING) {
						ch->sdl.stats.rx_overruns += 8 * 31;
						ch->sdt.stats.rx_overruns += 8 * 31;
					}
				}
				break;
			default:
			{
				static unsigned long throttle = 0;

				if (throttle + 10 <= (volatile unsigned long) jiffies)
					break;
				throttle = (volatile unsigned long) jiffies;
				swerr();
				break;
			}
			}
		}
	}
}

static noinline fastcall __hot void xp_t1_txrx_burst(struct cd *cd);
static noinline fastcall __hot void xp_t1_process_span(struct cd *cd, struct sp *sp);
static noinline fastcall __hot void xp_t1_process_timeout(struct cd *cd, struct sp *sp, volatile uint8_t *xlb,
							  register uint flags);
static noinline fastcall __hot void xp_t1_process_state(struct cd *cd, struct sp *sp,
							register volatile uint8_t *xlb,
							const register uint flags,
							const register uint errors);
static noinline fastcall __hot void xp_t1_process_stats(struct sp *sp, register uint errors);
static noinline fastcall __hot void xp_t1_process_alarms(struct cd *cd, struct sp *sp,
							 register volatile uint8_t *xlb, register uint flags,
							 uint timeout);
static noinline fastcall __hot void xp_t1_eval_syncsrc(struct cd *cd);
static noinline fastcall __unlikely void xp_t1_process_auto(struct cd *cd, struct sp *sp,
							    register volatile uint8_t *xlb,
							    register uint flags, const register uint errors,
							    const uint timeout);
noinline __unlikely int xp_t1_change_config(struct sp *sp, register volatile uint8_t *xlb);

/** xp_t1_interrupt: - T400P-SS7 Interrupt Service Routine
  * @irq: interrupt number
  * @dev_id: card structure pointer (opaque)
  * @regs: interrupted registers
  *
  * The user will always preceed a read of any fo the SR1, SR2, and RIR registers with a write.  The
  * user will write a byte to one of these registers, with a one in the bit positions he or she
  * wishes to read and a zero in the bit positions he or she does not wish to obtain the latest
  * information on.  When a one is written to a bit location, the read register will be updated with
  * the latest information.  When a zero is written to a bit position, the read register will not be
  * updated and the previous value will be held.  A write to the status and information registers
  * will be immediated followed by a read of the same register.  The read result should be logically
  * ANDed with the mask byte that was just written and this value should be written back to the same
  * register to insure that the bit does indeed clear.  This second write step is necessary because
  * the alarms and events in the statue registers occur asyncrhonously in respect to their access
  * via the parallel port.  This write-read-write scheme allows an external controller or
  * microprocessor to individually poll certain bits without disturbing the other bits in the
  * register.  This operation is key in controlling the DS21354/DS21554 wtih higher-order software
  * languages.
  *
  * Process an interrupt for a T400P card (with a DS21352/DS21552 chip).  The spans on this card can
  * only be E1 span.  This is a departure from the previous driver which counted frames to determine
  * timeout events: here we utilize the Dallas interrupts available on the card for non-frame
  * events.  This function should really be aligned on a page boundary and the functions that it
  * calls laid out immediately following the function, thus the forward declarations above.
  */
STATIC __hot irqreturn_t
#ifdef HAVE_KTYPE_IRQ_HANDLER_2ARGS
xp_t1_interrupt(int irq, void *dev_id)
#else
xp_t1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
	static unsigned long lasttime = 0;
	struct cd *cd = (struct cd *) dev_id;
	int safety;

	/* Keep another processor from entering the interrupt while another is already processing the
	   interrupt.  This means that the top half of the driver must disable interrupts when taking the
	   card lock to avoid deadly embrace. */
	spin_lock(&cd->lock);
	/* active interrupt (otherwise spurious or shared) */
	if (!(cd->xlb[STAREG] & (INTACTIVE | DINTACT))) {
		spin_unlock(&cd->lock);
		return (irqreturn_t) (IRQ_NONE);
	}
	if (likely(cd->xlb[STAREG] & INTACTIVE)) {
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | OUTBIT | DINTENA | INTACK);
		xp_t1_txrx_burst(cd);
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | DINTENA);
	}

	for (safety = 0; unlikely(cd->xlb[STAREG] & DINTACT); safety++) {
		uint span;
		struct sp *sp;

		/* We would like to try to keep this burning a loop here when there is only one span that is
		   responsible for the Dallas interrupt; however, the DS21352/552 chips do not support an
		   interrupt information register like the DS2155/455/458. */
		for (span = 0; span < cd->ports; span++)
			if (likely((sp = cd->spans[span]) != NULL))
				xp_t1_process_span(cd, sp);

		if (unlikely(safety > 8)) {
			/* throttle software error message */
			if (lasttime == 0 || (jiffies - lasttime) > HZ) {
				lasttime = jiffies;
				swerr();
			}
			break;
		}
	}
	/* Reevaluate the sync source when necessary.  Try to skip the call here whenever possible to keep
	   the ISR tight. */
	if (unlikely(xchg((int *) &cd->eval_syncsrc, 0) != 0)) {
		if (cd->config.ifsyncsrc[0] != 0)
			xp_t1_eval_syncsrc(cd);
	}
	spin_unlock(&cd->lock);
	return (irqreturn_t) (IRQ_HANDLED);
}

/** xp_t1_txrx_burst: - burst transfer TX and RX data
  * @cd: card structure pointer
  *
  * This function is responsible for transfering frame data to and from the card.  This is being
  * done with host driven I/O because the PLX9030 is incapable of bus mastering DMA.  When the
  * memory mapped I/O region is properly described, PCI burst transfers will occur.  Transfers are
  * 1024 bytes written and 1024 bytes read.  These are interleaved, but might better be performed as
  * 4 Lword interleaved transfers.
  *
  * Note that this transfer is somewhat different form the one used for E1 or E1/T1/J1 cards because
  * it can skip words that correspond to ILB slots that are not used by T1.
  */
static noinline fastcall __hot void
xp_t1_txrx_burst(struct cd *cd)
{
	int lebno;

	if ((lebno = (cd->lebno + 1) & (X400P_EBUFNO - 1)) != cd->uebno) {
		register int slot;
		register volatile uint32_t *xll;
		register const uint32_t *wbuf = cd->wbuf + (lebno << 8);
		register const uint32_t *const wend = wbuf + 256;
		register uint32_t *rbuf = cd->rbuf + (lebno << 8);

		cd->lebno = lebno;
		for (xll = cd->xll; wbuf < wend;)
			for (wbuf++, rbuf++, xll++, slot = 1; slot < 32; slot++, xll++, wbuf++, rbuf++)
				if (slot & 0x3) {
					prefetch(wbuf + 2);
					prefetchw(rbuf + 2);
					*xll = *wbuf;
					*rbuf = *xll;
				}
		tasklet_hi_schedule(&cd->tasklet);
	} else
		xp_overflow(cd);
}

/** xp_t1_process_span: - process Dallas interrupts for a single span
  * @cd: card structure pointer
  * @sp: span structure pointer
  *
  * PERFORMANCE DEFECTS:
  *
  * XP_DEF_AIS: Alarm Indication Signal (AIS) Defect:
  *	For D4 and ESF links, the 'all ones' condition is detected at a DS1 line interface upon
  *	observing an unframed signal with a one's density of at least 99.9 percent present for a
  *	time equal to or greater than T, where 3ms is less than or equal to T, which is less than or
  *	equal to 75ms.  The AIS is terminated upon observing a signal not meeting the one's density
  *	of the unframed signal criteria for a period equal to or greater than T.  For E1 links, the
  *	'all-ones' condition is detected at the line interface as a string of 512 bits containing
  *	fewer than three zero bits. 
  *
  * XP_DEF_OOF: Out of Frame (OOF) Defect:
  *	An OOF defect is the occurrence of a particular density of Framing Error events.  For T1
  *	links, an OOF defect is declared when the receiver detects two or more framing errors within
  *	a 3 ms period for ESF signals and 0.75 ms for D4 signals, or two or more errors out of five,
  *	or fewer consecutive framing-bits.  For E1 links, an OOF defect is declared when three
  *	consecutive frame alignment signals have been received with an error.  When an OOF defect is
  *	declared, the frame starts searching for a correct pattern.  The OOF defect ends when the
  *	signal is in-frame.  In-frame occurs when there are fewer than two frame bit errors within a
  *	3 ms period for ESF signals and 0.75 ms for D4 signals.  For E1 links, in-frame occurs when
  *	in frame N, the frame alignment signal is correct; and, in frame N+1, the frame alignment
  *	signal is absent (that is, bit 2 in TS0 is set to one); and, in frame N+2, the frame
  *	alignment signal is present and correct. 
  *
  * FAILURE STATES: The following failure states are received or detected failures that are
  * reported.  The conditions under which a DS1 interface would, if ever, produce the conditions
  * leading to the failure state are described in the appropriate specification.
  *
  * XP_FAIL_AIS: Alarm Indication Signal (AIS) Failure:
  *	The AIS failure is declared when an AIS defect is detected at the input and the AIS defect
  *	still exists after the LOF failure (which is caused by the unframed nature of the all-ones
  *	signal) is detected.  The AIS failure is cleared when the LOF failure is cleared.  
  *
  * XP_FAIL_FEA: Far End Alarm Failure (Yellow Alarm):
  *	The Far End Alarm failure is also known as Yellow Alarm in the T1 cases and Distant Alarm
  *	(or RAI) in the E1 case.  For D4 links, the FEA failure is declared when bit 6 of all
  *	channels have been zero for at least 335 ms and is cleared when bit 6 of at least one
  *	channel is non-zero for a period T, where T is usually less than one second and alway less
  *	than five seconds.  The FEA failure is not declared for D4 links when LOS is detected.  For
  *	ESF links, the FEA failure is declared if the Yellow Alarm signal pattern occurs in a least
  *	seven out of ten contiguous 16-bit-pattern intervals and is cleared if the Yellow Alarm
  *	signal pattern does not occur in ten contiguous 16-bit signal pattern intervals.  For E1
  *	links, the FEA failure is declared when bit 3 of time-slot zero is set to one on two
  *	consecutive occasions.  The FEA failure is cleared when bit 3 of time-slot zero is received
  *	set to zero.  
  *
  * XP_FAIL_FELOM: Far End Loss of Multiframe Failure:
  *	The FELOM failure is declared when bit 2 of TS16 of frame 0 is received set to one on two
  *	consecutive occasions.  The FELOM failure is cleared when bit 2 of TS16 of frame 0 is
  *	received set to zero.  The FELOM failure can only be declared for E1 links operating in CAS
  *	mode.  
  *
  * XP_FAIL_LPF: Loopback Pseudo-Failure:
  *	The LPF is declared when the near end equipment has placed a loop-back (of any kind) on the
  *	DS1.  This allows a management entity to determine from one object whether the DS1 can be
  *	considered to be in service or not (from the point of view of the near-end equipment).  
  *
  * XP_FAIL_LOF: Loss of Frame (LOF) Failure:
  *	For T1 links, the LOF failure is declared with an OOF or LOS defect has persisted for T
  *	seconds, where T is greater than or equal to two, but less than or equal to ten.  The LOF
  *	failure is cleared when there have been no OOF or LOS defects during a period T is greater
  *	than or equal to two, but less than or equal to twenty.  Many systems will perform "hit
  *	integration" with the period T before declaring or clearing the failure. 
  *
  * XP_FAIL_LOM: Loss of Multiframe (LOM) Failure:
  *	The LOM failure is declared when two consecutive multi-frame alignment signals (bit 4 through 7 of TS16 of
  *	frame 0) have been received wtih an error.  The LOM failure is cleared when the first
  *	correct multi-frame alignment signal is received.  The LOM failure can only be declared for
  *	E1 links operating with framing (sometimes called CAS mode).  
  *
  * XP_FAIL_LOS: Loss of Signal (LOS) Failure:
  *	For T1, the Loss of Signal failure is declared upon observing 175 +/- 74 contiguous pulse
  *	position with no pulses of either positive or negative polarity.  The LOS failure is cleared
  *	upon observing an average pulse density of at least 12.5 percent over a period of 175 +/- 74
  *	contiguous pulse positions starting with the receipt of a pulse.  
  *
  * XP_FAIL_T16AIS: T16 Alarm Indication Signal Failure:
  *	For E1 links, the Ts16 Alarm Indication Signal failure is declared when time-slot 16 is
  *	received as all ones for all frames of two consecutive multi-frames.  This condition
  *	is never declared for T1.  
  */
static noinline fastcall __hot void
xp_t1_process_span(struct cd *cd, struct sp *sp)
{
	register volatile uint8_t *xlb = (typeof(xlb)) sp->iobase;
	register uint8_t status, mask;
	register uint errors = sp->status.errors;
	register uint flags = sp->status.flags;
	uint timeout = 0;

	if ((mask = xlb[0x7f])) {	/* IMR1 */
		/* write-read-write cycle */
		xlb[0x20] = 0xff;
		status = xlb[0x20] & 0xff;
		xlb[0x20] = status;
		/* SR1.7: LUP: Loop Up Code Detected.  Set when the loop up code as defined in the RUPCD
		   register is being received. */
		if (status & (1 << 7)) {
			flags |= XP_STATE_LUP;
		} else if (mask & (1 << 7)) {
			flags &= ~XP_STATE_LUP;
		}
		/* SR1.6: LDN: Loop Down Code Detected. Set when the loop down code as defined in the RDNCD
		   register is being received. */
		if (status & (1 << 6)) {
			flags |= XP_STATE_LDN;
		} else if (mask & (1 << 6)) {
			flags &= ~XP_STATE_LDN;
		}
		/* SR1.5: LOTC: Loss of Transmit Clock.  Set when the TCLK pin has not transitioned for one
		   channel time (or 5.2us).  Will force the RLOS/LOTC pin high if enabled via CCR1.6.  Also
		   will force transmit side formatter to switch to RCLK if so enabled via TCR1.7.  */
		if (status & (1 << 5)) {
			flags |= XP_STATE_LOTC;
		} else if (mask & (1 << 5)) {
			flags &= ~XP_STATE_LOTC;
		}
		/* SR1.4: RSLIP: Receive Elastic Store Slip Occurrence.  Set when the receive elastic store
		   has either repeated or deleted a frame.  */
		if (status & (1 << 4)) {
			errors |= XP_ERR_CSS;
		}
		/* SR1.3: RBL: Receive Blue Alarm.  Set when an unframed all one's code is received at RPOSI
		   and RNEGI. */
		if (status & (1 << 3)) {
			flags |= XP_STATE_RUA1;
		} else if (mask & (1 << 3)) {
			flags &= ~XP_STATE_RUA1;
		}
		/* SR1.2: RYEL: Receive Yellow Alarm.  Set when a yellow alarm is received at RPOSI and
		   RNEGI.  */
		if (status & (1 << 2)) {
			flags |= XP_STATE_RYEL;
		} else if (mask & (1 << 2)) {
			flags &= ~XP_STATE_RYEL;
		}
		/* SR1.1: LRCL: Line Interface Receive Carrier Loss.  Set when a red alarm is received at
		   RTIP and RRING.  */
		if (status & (1 << 1)) {
			flags |= XP_STATE_LRCL;
		} else if (mask & (1 << 1)) {
			flags &= ~XP_STATE_LRCL;
		}
		/* SR1.0: RLOS: Receive Loss of Sync.  Set when the device is not syncrhonized to the receive 
		   T1 stream. */
		if (status & (1 << 0)) {
			flags |= XP_STATE_RLOS;
		} else if (mask & (1 << 0)) {
			flags &= ~XP_STATE_RLOS;
		}
	}

	if ((mask = xlb[0x6f])) {	/* IMR2 */
		/* write-read-write cycle */
		xlb[0x21] = 0xff;
		status = xlb[0x21] & 0xff;
		xlb[0x21] = status;
		/* SR2.7: RMF: Receive Multiframe.  Set on receive multiframe boundaries. */
		if (status & (1 << 7)) {
		} else if (mask & (1 << 7)) {
		}
		/* SR2.6: TMF: Transmit Multiframe. Set on transmit multiframe boundaries.  */
		if (status & (1 << 6)) {
		} else if (mask & (1 << 6)) {
		}
		/* SR2.5: SEC: One Second Timer.  Set on increments of one second based on RCLK; will be set
		   in increments of 999ms, 999ms and 1002ms every 3 seconds.  */
		if (status & (1 << 5)) {
			timeout = 1;
		}
		/* SR2.4: RFDL: Receive FDL Buffer Full.  Set when the receive FDL buffer (RFDL) fills to
		   capacity (8 bits). */
		if (status & (1 << 4)) {
		} else if (mask & (1 << 4)) {
		}
		/* SR2.3: TFDL: Transmit FDL Buffer Empty.  Set when the transmit FDL bfufer (TFDL) empties. */
		if (status & (1 << 3)) {
		} else if (mask & (1 << 3)) {
		}
		/* SR2.2: RMTCH: Receive FDL Match Occurence.  Set when the RFDL matches either RFDLM1 or
		   RFDLM2.  */
		if (status & (1 << 2)) {
		} else if (mask & (1 << 2)) {
		}
		/* SR2.1: RAF: Receive FDL Abort.  Set when eight consecutive one's are received in the FDL. */
		if (status & (1 << 1)) {
		} else if (mask & (1 << 1)) {
		}
		/* SR2.0: RSC: Receive Signaling Change. Set when the DS21352/DS21552 detects a change of
		   state in any of the robbed-bit signalling bits.  */
		if (status & (1 << 0)) {
		} else if (mask & (1 << 0)) {
		}
	}

	/* Note: at this point, any Dallas interrupt should have been cleared by reading the interrupt bits.
	   Dallas interrupts generated by the IMR registers are acknowledged by reading the corresponding bit 
	   of the SR registers. */

	if (timeout)
		xp_t1_process_timeout(cd, sp, xlb, flags);

	/* Note that alarms are processed asyncrhonously whereas interface state is only processed during
	   timeouts. This keeps the interface state from thrashing around and detection mechanisms falsely
	   triggering on transient conditions. */
	if ((flags ^ sp->status.flags) != 0)
		xp_t1_process_alarms(cd, sp, xlb, flags, timeout);
}

/** xp_t1_process_timeout: - process timeout for a span
  * @cd: card structure (locked)
  * @sp: span structure
  * @xlb: span iobase pointer
  * @flags: span flags
  */
static noinline fastcall __hot void
xp_t1_process_timeout(struct cd *cd, struct sp *sp, volatile uint8_t *xlb, register uint flags)
{
	register uint mask, status;
	register uint errors = sp->status.errors;
	uint count;

	/* Read information registers and report */
	mask = 0xff;
	/* write-read-write cycle */
	xlb[0x22] = mask;
	status = xlb[0x22] & mask;
	xlb[0x22] = status;
	if (status) {
		/* RIR1.7: COFA: change of Frame Alignment.  Set when the last resync resulted in a change of 
		   multiframe alignment.  */
		/* RIR1.0: FBE: Frame Bit Error.  Set when a Ft (D4) or FPS (ESF) framing bit is received in
		   error. Note that the count of these errors can be returned in the MOSCR error counter. */
		if (status & ((1 << 7) | (1 << 0))) {
			errors |= XP_ERR_FASE;
		}
		/* RIR1.6: 8ZD: Eight Zero Detect.  Set when a string of at least eight consecutive zeros
		   (regardless of the length of the string) have been received at RPOSI and RNEGI.  Note that 
		   the count of these errors can be returned in the LCVCR error counter. */
		/* RIR1.5: 16ZD: Sixteen Zero Detect. Set when a string of at least sixteen consecutive zeros 
		   (reagardless of the length of the string) have been received at RPOSI and RNEGI.  Note
		   that the count of these errors can be returned in the LCVCR error counter.  */
		if (status & ((1 << 6) | (1 << 5))) {
			errors |= XP_ERR_LCV;
		}
		/* RIR1.4: RESF: Receive Elastic Store Full.  Set when the receive elastic store buffer fills 
		   and a frame is deleted.  */
		/* RIR1.3: RESE: Receive Elastic Store Empty.  Set when the receive elasitc store buffer
		   emtpies and a frame is repeated.  */
		if (status & ((1 << 4) | (1 << 3))) {
			errors |= XP_ERR_CSS;
		}
		/* RIR1.2: SEFE: Severely Errorred Framing Event.  Set when 2 out of 6 framing bits (Ft or
		   FPS) are received in error.  */
		if (status & (1 << 2)) {
			errors |= XP_ERR_SEFS;
		}
		/* RIR1.1: B8ZS: B8ZS Code Word Detect.  Set when a B8ZS code word is detected at RPOSI and
		   RNEGI independent of whether the B8ZS mode is selected or not via CCR2.6.  Useful for
		   automatically setting the line coding.  */
		if (status & (1 << 1)) {
			/* A B8ZS codeword was detected.  When we are set to T1 AMI and there are no bipolar
			   violations, and a B8ZS codeword is detected, this certainly means that the line
			   coding should be B8ZS instead of AMI.  However, SS7 links are always B8ZS. */
			if (sp->config.ifcoding == SDL_CODING_AMI) {
				errors |= XP_ERR_B8ZS;
			}
		}
	}

	mask = ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0));
	/* write-read-write cycle */
	xlb[0x31] = mask;
	status = xlb[0x31] & mask;
	xlb[0x31] = status;
	if (status) {
		/* RIR2.7: RLOSC: Receive Loss of Sync Clear.  Set when the framer achieves synchronization;
		   will remain set until read. */
		if (status & (1 << 7))
			flags &= ~XP_STATE_RLOS;
		/* RIR2.6: LRCLC: Line Interface Receive Carrier Lost Clear.  Set when the carrier signal is
		   restored; will reamin set util read.  */
		if (status & (1 << 6))
			flags &= ~XP_STATE_LRCL;
		/* RIR2.5: TESF: Transmit Elastic Store Full.  Set when the transmit elastic store buffer
		   fills and a frame is deleted. */
		/* RIR2.4: TESE: Transmit Elastic Store Empty. Set when the transmit elastic store buffer
		   empties and a frame is repeated. */
		/* RIR2.3: TSLIP: Transmit Elastic Store Slip Occurrence. Set when the transmit elastic store 
		   has either repeated or deleted a frame.  */
		if (status & ((1 << 5) | (1 << 4) | (1 << 3)))
			errors |= XP_ERR_CSS;
		/* RIR2.2: RBLC: Receive Blue Alarm Clear. Set when the blue alarm (AIS) is no longer
		   detected; will remain set until read. */
		if (status & (1 << 2))
			flags &= ~XP_STATE_RUA1;
		/* RIR2.1: RPDV: Receive Pulse Density Violation.  Set when the receive data stream does not
		   meet the ANSI T1.403 requirements for pulse density.  */
		/* RIR2.0: TPDV: Transmit Pulse Density Violation. Set when the transmit data stream does not 
		   meet the ANSI T1.403 requirements for pulse density.  */
		if (status & ((1 << 1) | (1 << 0)))
			errors |= XP_ERR_PDV;
	}

	mask = ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3));
	/* write-read-write cycle */
	xlb[0x10] = mask;
	status = xlb[0x10] & mask;
	xlb[0x10] = status;
	sp->config.ifrxlevel = ((status & 0xc0) >> 6);
	if (status) {
		/* RIR3.5: JALT: Jitter Attenuation Trip Limit.  Set when the jitter attenuator FIFO reaches
		   to within 4 bits of its limit; useful for debugging jitter attenuation operation. */
		if (status & (1 << 5)) {
			flags |= XP_STATE_JALT;
			errors |= XP_STATE_JALT;
		}
		/* RIR3.4: LORC: Loss of Receive Clock.  Set when the RCLKI pin has not transitioned for at
		   least 2us (4 us max).  */
		if (status & (1 << 4)) {
			flags |= XP_STATE_LORC;
			errors |= XP_STATE_LORC;
		} else
			flags &= ~XP_STATE_LORC;
		/* RIR3.3: FRCL: Framer Receive Carrier Loss.  Set when 192 consecutive zeroes have been
		   received at RPOSI and RNEGI pins; allowed to be cleared when 14 or more ones out of 112
		   possible bit positions are received.  */
		if (status & (1 << 3)) {
			flags |= XP_STATE_FRCL;
			errors |= XP_STATE_FRCL;
		} else
			flags &= ~XP_STATE_FRCL;
		/* RIR3.7-6: RL1-RL0: Receive Level. 00, +2dB to -7.5dB; 01, -7.5dB to -15dB; 10, -15dB to
		   -22.5dB; 11, less than -22.5dB. */
		if ((status & ((1 << 7) | (1 << 6))) == ((1 << 7) | (1 << 6)))
			flags |= XP_STATE_ILUT;
		else
			flags &= ~XP_STATE_ILUT;
	}

	mask = ((1 << 2) | (1 << 3) | (1 << 4));
	/* write-read-write cycle */
	xlb[0x22] = mask;	/* RIR1 */
	status = xlb[0x22] & mask;
	xlb[0x22] = status;
	if (status) {
		/* RIR1.4: RESF: Receive Elastic Store Full. */
		/* RIR1.3: RESE: Receive Elastic Store Empty. */
		if ((mask & ((1 << 3) | (1 << 4))) && (status & ((1 << 3) | (1 << 4)))) {
			errors |= XP_ERR_CSS;	/* controlled slip */
			errors |= XP_ERR_ES;
		}
		/* RIR1.2: SEFE: Severely Errorred Framing Event. */
		if ((mask & (1 << 2)) && (status & (1 << 2))) {
			errors |= XP_ERR_SEFS;	/* severely errored frame */
		}
	}

	mask = ((1 << 3) | (1 << 4) | (1 << 5));
	/* write-read-write cycle */
	xlb[0x31] = mask;
	status = xlb[0x31] & mask;
	xlb[0x31] = status;
	if (status) {
		/* RIR2.5: TESF: Transmit Elastic Store Full. */
		/* RIR2.4: TESE: Transmit Elastic Store Empty. */
		/* RIR2.3: TSLIP: Transmit Elastic Store Slip Occurrence. */
		if ((mask & ((1 << 3) | (1 << 4) | (1 << 5)))
		    && (status & ((1 << 3) | (1 << 4) | (1 << 5)))) {
			errors |= XP_ERR_CSS;	/* controlled slip */
			errors |= XP_ERR_ES;
		}
	}

	/* The number of Line Code Violations (LCVs) in the current interval.  An LCV is the occurrence of a
	   Bipolar Violation (BPV) or Excessive Zeros (EXZ) error event. */
	/* RCR1.7: LCVCRF: Line Code Violation Count Register Function Select. 0, do not count excessive
	   zeros; 1, count excessive zeros. */
	/* The 16-bit line code violation LCV count register reports code violations (CV). CV are defined as
	   Bipolar Violations (BPVs) or excessive zeros.  If the B8ZS mode is set for the receive side via
	   CCR2.2, then B8ZS code words are not counted. This counter is always enabled; it is not disabled
	   during receive loss of synchronization (RLOS=1) conditions. */
	if ((count = (((uint) xlb[0x23] << 8) + ((uint) xlb[0x24])))) {
		sp->stats[0].LCVs += count;
		errors |= XP_ERR_LCV;	/* line code violation */
	}
	/* The number of Path Coding Violations (PCVs) in the current interval. A Path Coding Violation is a
	   frame synchronization bit error in the D4 and E1 no-CRC4 formats, or a CRC or frame
	   synchronization bit error in the ESF and E1 CRC4 formats. */
	/* RCR2.1: FSBE: PCVCR Fs-Bit Error Report Enable.  0, do not report bit errors in Fs-bit position;
	   only Ft bit position; 1, report bit errors in Fs-bit position as well as Ft-bit position. */
	/* The 12-bit path code violation (PCV) counter records errors in the CRC6 code words. When set to
	   operate in the D4 framing mode (CCR2.3=0), PCVCR will automatically count errors in the Ft framing 
	   bit position.  Via the RCR2.1 bit, a framer can be programmed to also report errors in the Fs
	   framing bit position. The PCVCR will be disabled during receive loss of synchronization (RLOS=1)
	   conditions. */
	/* CCR2.3=D4, RCR2.1=no, errors in the Ft pattern. */
	/* CCR2.3=D4, RCR2.1=yes, errors in both the Ft and Fs pattern. */
	/* CCR2.3=ESF, RCR2.1=don't care, errors in the CRC6 code words. */
	/* NOTE: when in the ESF mode, a steady stream of PCVs might indicate that CRC6J should be set
	   instead of CRC6, or vise versa. */
	if ((count = (((uint) (xlb[0x25] & 0x0f) << 8) + ((uint) xlb[0x26])))) {
		sp->stats[0].PCVs += count;
		errors |= XP_ERR_PCV;	/* path code violation */
		errors |= XP_ERR_ES;
	}
	/* the number of Multi-frames Out Of Sync(LOFs) in the current interval. A Loss of Frame count (LOFs) 
	   is the number of multi-frames that the receive synchronizer is out of sync. This may also be a
	   count of errors in the Ft framing pattern in D4 mode, or errors in the FPS framing patter in the
	   ESF mode. */
	/* RCR2.0: MOSCRF: Multi-frame Out of Sync Count Register Function Select.  0, count errors in the
	   framing bit position; 1, count the number of multi-frames out of sync. */
	if ((count = (((uint) (xlb[0x25] & 0xf0) << 4) + ((uint) xlb[0x27])))) {
		sp->stats[0].FASEs += count;
		errors |= XP_ERR_FASE;
		errors |= XP_ERR_ES;
	}

	xp_t1_process_state(cd, sp, xlb, flags, errors);

	/* CCR3.2: ECUS: 0 = 8000 frames, 1 = 333 frames */
	if ((sp->interval += (xlb[0x30] & 0x04) ? 333 : 8000) >= 8000) {
		sp->interval -= 8000;
		/* this is a 1 second timeout */
		xp_t1_process_stats(sp, errors);
		sp->status.errors = 0;
	} else
		sp->status.errors = errors;
	return;
}

/** xp_t1_process_state: - process state transitions for the device.
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span iobase
  * @flags: state flags (read only)
  * @errors: error flags (read only)
  *
  * Process state transitions once a timeout.  This is to ensure that the interface state does not
  * change due to transient conditions of less than 40 ms.  Also, any events or errors here have
  * accumulated for the entire timeout period (more than 40 ms).
  *
  * Processing DS21352/552 state transitions is more difficult because there is no transmitter
  * open-circuit detection capability on the chip, making transmitter state difficult to determine.
  * Also, receiver input level determination is rather limited in range.  Both these deficiencies
  * make it difficult to automatically determine whether a span is in a monitor configuration or not
  * and whether linear gain should be applied to the receivers.
  *
  * The approach is to take the transmitters offline altogether, and check whether a synced receive
  * side is indicating a yellow alarm.  When the receiver is synced and there is no yellow alarm,
  * the transmitters are not connected to the line.  When the receivers won't sync but there is no
  * carrier loss, we can use the input level somewhat, and possibly some errors, to determine
  * whether linear gain should be applied.  When there is no carrier for some time, we can go ahead
  * and try applying linear gain.  A new line interface receive carrier loss should start the
  * process all over again so that when a line is disconnected and then reconnected, it will
  * autoconfigure again.
  *
  * To perform all of these actions, we run a state machine and keep a state variable in
  * sp->status.state.  A state transition is performed at most once every 42/62 ms (short timeout).
  * The current state and the condition of the state flags and current errors are used to determine
  * the next state.
  */
static noinline fastcall __hot void
xp_t1_process_state(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, const register uint flags, const
		    register uint errors)
{
	register uint ifflags = sp->config.ifflags;

	/* State flags that we set: XP_STATE_LUP, XP_STATE_LDN, XP_STATE_LOTC, XP_STATE_RUA1, XP_STATE_RYEL,
	   XP_STATE_LRCL, XP_STATE_RLOS, XP_STATE_LORC, XP_STATE_FRCL, XP_STATE_ILUT (but not as good as
	   DS2155). */

	/* integrate receiver state */
	if (flags & (XP_STATE_LRCL | XP_STATE_FRCL)) {
		ifflags &= ~SDL_IF_RX_UP;
		ifflags &= ~SDL_IF_RX_RUNNING;
	} else {
		ifflags |= SDL_IF_RX_UP;
		if (flags & (XP_STATE_RLOS | XP_STATE_RUA1 | XP_STATE_LORC))
			ifflags &= ~SDL_IF_RX_RUNNING;
		else
			ifflags |= SDL_IF_RX_RUNNING;
	}

	/* Note the DS21352/552 does not actually provide open-circuit or short-circuit transmitter
	   detection. The flags are there for the DS2155/455/458 chips.  To establish that the transmitters
	   are open-circuited we need to transmit unframed all ones and detect the remote AIS indication from 
	   the far end.  In fact, the only semi-useful flags we have is XP_STATE_LOTC. */

	/* integrate transmitter state */
	if (flags & (XP_STATE_TOCD | XP_STATE_TSCD)) {
		ifflags &= ~SDL_IF_TX_UP;
		ifflags &= ~SDL_IF_TX_RUNNING;
	} else {
		ifflags |= SDL_IF_TX_UP;
		if (flags & (XP_STATE_LOLITC | XP_STATE_LOTC))
			ifflags &= ~SDL_IF_TX_RUNNING;
		else
			ifflags |= SDL_IF_TX_RUNNING;
	}
	if (!(ifflags & SDL_IF_RX_UP)) {
		ifflags &= ~SDL_IF_TX_UP;
		ifflags &= ~SDL_IF_TX_RUNNING;
	}

	/* integrate interface state */
	if (flags & (SDL_IF_RX_UP | SDL_IF_TX_UP))
		ifflags |= SDL_IF_UP;
	else
		ifflags &= ~SDL_IF_UP;

	/* detect changes in transmitter state */
	if ((ifflags ^ sp->config.ifflags) & (SDL_IF_TX_UP | SDL_IF_TX_RUNNING)) {
	}

	/* detect changes in receiver state */
	if ((ifflags ^ sp->config.ifflags) & (SDL_IF_RX_UP | SDL_IF_RX_RUNNING)) {
	}

	/* detect changes in interface state */
	if ((ifflags ^ sp->config.
	     ifflags) & (SDL_IF_UP | SDL_IF_TX_UP | SDL_IF_RX_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)) {
	}

	/* perform autoconfiguration when necessary */
	if (ifflags & SDL_IF_AUTOCONFIG) {
		if (likely(sp->status.state == 40)) {
			if (unlikely((flags & (XP_STATE_LRCL)) != 0))
				/* lost carrier, start full autoconfig */
				sp->status.state = 0;
			else if (unlikely((flags & (XP_STATE_FRCL)) != 0))
				/* lost signal, start partial autoconfig */
				sp->status.state = 20;
		}
		if (unlikely(sp->status.state != 40))
			/* lost configuration hold, process autoconfig */
			xp_t1_process_auto(cd, sp, xlb, flags, errors, 1);
	}
}

/** xp_t1_process_auto: - process autoconfiguration state machine
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span iobase
  * @flags: state flags
  * @errors: error events
  * @timeout: true when timeout
  *
  * Note that we do not even execute this function unless SDL_IF_AUTOCONFIG is set in the interface
  * flags.  The function is executed on regular interrupts as well as timeout interrupts.  The
  * @timeout argument is set when the interrupt was as a result of a timeout, and Unset otherwise.
  * This permits having the autodetection state machine run very quickly for expected
  * configurations.
  *
  * The DS21352/552 chip can perform linear gain for monitoring applications of 12dB or 20dB.  20dB
  * corresponds to 432-470 Ohm isolation resistors on a single high-impedance monitoring tap.  12dB
  * linear gain (corresponding to 150 Ohm isolation resistors) is not useful for monitoring
  * applications.
  */
static noinline fastcall __unlikely void
xp_t1_process_auto(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint flags, const
		   register uint errors, const uint timeout)
{
	register uint state = sp->status.state;
	const uint interval = timeout ? ((xlb[0x30] & 0x04) ? 333 : 8000) : 0;

	for (;;) {
		do {
			if (flags & XP_STATE_LIRST) {
				/* resetting line interface */
				sp->status.count = 0;
				sp->status.config = 0;
				state = 0;
				break;
			}
			if ((flags & XP_STATE_LRCL) && (state >= 8)) {
				/* lost line carrier */
				sp->status.count = 0;
				state = 0;
				break;
			}
			if ((flags & XP_STATE_FRCL) && (state >= 9)) {
				/* lost framer signal */
				sp->status.count = 0;
				state = 8;
				break;
			}
			if ((flags & XP_STATE_RUA1) && (state >= 10)) {
				/* lost service */
				sp->status.count = 0;
				state = 9;
				break;
			}
			if ((flags & XP_STATE_RLOS) && (11 <= state && state < 13)) {
				/* lost sync */
				sp->status.count = 0;
				state = 10;
				break;
			}
		} while (0);
		switch (state) {
		default:
			state = 0;
			/* fall through */
		case 0:
			/* LIRST subsidance.  Wait 125 ms or more when the LIRST condition remains, then
			   clear the condition and move on to line detection.  */
			if (flags & (XP_STATE_LIRST)) {
				if ((sp->status.count += interval) < 1000) {
					/* wait for up to 125 ms */
					break;
				}
				/* remove LIRST flag */
				sp->status.flags &= ~(XP_STATE_LIRST);
				flags &= ~(XP_STATE_LIRST);
				/* reset elastic stores */
				/* CCR7.5: RESR: Receive Elastic Store Reset.  toggle to reset. */
				/* CCR7.4: TESR: Transmit Elastic Store Reset.  toggle to reset. */
				xlb[0x0a] &= ~((1 << 5) | (1 << 4));
				xlb[0x0a] |= (1 << 5) | (1 << 4);
				xlb[0x0a] &= ~((1 << 5) | (1 << 4));
				/* realign elastic stores */
				/* CCR6.6: RESA: Receive Elastic Store Align. toggle to align. */
				/* CCR6.5: TESA: Transmit Elastic Store Align. toggle to align. */
				xlb[0x1e] &= ~((1 << 6) | (1 << 5));
				xlb[0x1e] |= (1 << 6) | (1 << 5);
				xlb[0x1e] &= ~((1 << 6) | (1 << 5));
			}
			/* remove linear gain */
			xlb[0x09] = 0x00;
			/* set 0dB CSU */
			xlb[0x7c] &= ~((1 << 7) | (1 << 6) | (1 << 5));
			/* start transmitter */
			/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
			xlb[0x7c] &= ~(1 << 0);
			/* send unframed all ones */
			/* TCR1.1=0: TBL: Transmit Blue Alarm. 0 = normal; 1 = tx unframed all ones. */
			xlb[0x35] |= (1 << 1);
			/* set count and flags */
			sp->status.count = 0;
			sp->config.ifflags &= ~(SDL_IF_UP | SDL_IF_RX_UP | SDL_IF_TX_UP | SDL_IF_RX_MON);
			state = 1;
			/* fall through */
		case 1:
			/* Line carrier detect, 0dB gain. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying linear gain */
					/* apply 12dB linear gain */
					xlb[0x09] = 0x72;
					sp->status.count = 0;
					state = 2;
					continue;
				}
				/* wait for up to 125 ms */
				break;
			}
			/* start transmitter */
			/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
			xlb[0x7c] &= ~(1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 2:
			/* Line carrier detect, monitoring, 12dB gain.  No line carrier was detected with 0dB 
			   linear gain, try applying 12dB gain.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying more linear gain */
					/* apply 20dB linear gain */
					xlb[0x09] = 0x70;
					sp->status.count = 0;
					state = 3;
					continue;
				}
				/* wait for up to 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 5;
			continue;
		case 3:
			/* Line carrier detect, monitoring, 20dB gain.  No line carrier was detected with 0dB 
			   and 12dB linear gain, try applying 20dB gain.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try removing gain again */
					/* apply 0dB linear gain */
					xlb[0x09] = 0x00;
					sp->status.count = 0;
					state = 1;
					continue;
				}
				/* wait for up to 125 ms */
				break;
			}
			/* apply 12dB linear gain */
			sp->status.count = 0;
			state = 4;
			/* fall through */
		case 4:
			/* Line carrier detected, monitoring, 12dB gain.  20dB gain was applied previously
			   with success, drop back to 12dB gain to test that things were not just connected
			   at a bad time. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 20dB gain */
					/* apply 20dB linear gain */
					xlb[0x09] = 0x70;
					sp->status.count = 0;
					state = 6;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* apply 0dB linear gain */
			sp->status.count = 0;
			state = 5;
			/* fall through */
		case 5:
			/* Line carrier detected, monitoring, 0dB gain.  12dB gain was applied previously
			   with success, drop back to 0dB gain to test that things were not just connected at 
			   a bad time. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 12dB gain */
					/* apply 12dB linear gain */
					xlb[0x09] = 0x72;
					sp->status.count = 0;
					state = 7;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* start transmitter */
			/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
			xlb[0x7c] &= ~(1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 6:
			/* Line carrier detected, monitoring, 20dB gain.  20dB worked, 12dB didn't, go back
			   to 20dB and lock in if successful. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 20dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					xlb[0x09] = 0x00;
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* stop transmitter */
			/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
			xlb[0x7c] |= (1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 7:
			/* Line carrier detected, monitoring, 12dB gain.  12dB worked, 0dB didn't, go back to 
			   12dB and lock in if successful. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 12dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					xlb[0x09] = 0x00;
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* stop transmitter */
			/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
			xlb[0x7c] |= (1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 8:
			/* Framer signal detect.  Line carrier detected, 0dB, 12dB or 20dB linear gain
			   applied.  When other than 0dB linear gain is applied, SDL_IF_RX_MON flag is set. */
			if (flags & (XP_STATE_FRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* no framer signal, start again */
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 9;
			/* fall through */
		case 9:
			/* Framing detect, sending unframed all ones.  If we are in a loop-around mode we
			   will wait here for manual (specific) configuration because we will be both sending 
			   and receiving unframed all ones.  However, only wait for 10 seconds and then
			   restart in case there is an error in the state machine that locks us here. */
			if (flags & (XP_STATE_RUA1)) {
				if ((sp->status.count += interval) >= 10 * 8000) {
					/* no service for 10 seconds, start again */
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait up to 10 seconds */
				break;
			}
			sp->status.count = 0;
			state = 10;
			/* fall through */
		case 10:
			/* Frame detect, not receiving unframed all ones. */
			if (flags & (XP_STATE_RLOS)) {
				if ((sp->status.count += interval) >= 3200) {
					/* no sync after 400 ms */
					/* reconfigure and try again */
					if (xp_t1_change_config(sp, xlb)) {
						/* exhausted possibilities, start again */
						sp->status.count = 0;
						state = 0;
						continue;
					}
					sp->status.count = 0;
					/* wait to test possibility */
					break;
				}
				/* wait for up to 400 ms */
				break;
			}
			sp->status.count = 0;
			state = 11;
			/* fall through */
		case 11:
			/* Transmitter detection, transmitting unframed all ones.  We are transmitting
			   unframed all ones, so when there is no remote alarm after 125 ms the transmitters
			   must be disconnected. */
			if (!(flags & (XP_STATE_RYEL | XP_STATE_RRA))) {
				if ((sp->status.count += interval) >= 1000) {
					/* No alarm after 125 milliseconds, transmitters must be
					   disconnected.  When no linear gain is applied, but transmitters
					   are disconnected: this is a 0dB active monitoring tap.  When
					   linear gain applied, already monitoring so this is an expected
					   result.  */
					/* stop transmitter */
					/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
					xlb[0x7c] |= (1 << 0);
					sp->config.ifflags |= SDL_IF_RX_MON;
					sp->config.ifflags &= ~(SDL_IF_TX_UP);
					sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
					sp->status.count = 0;
					state = 13;
					continue;
				}
				/* wait up to 125 ms */
				break;
			}
			/* transmit normal frames */
			/* TCR1.1=0: TBL: Transmit Blue Alarm. 0 = normal; 1 = tx unframed all ones. */
			xlb[0x35] &= ~(1 << 1);
			sp->status.count = 0;
			state = 12;
			/* fall through */
		case 12:
			/* Transmitter detection, sending normal frames.  We previously had a remote alarm
			   condition when sending unframed all ones.  We start sending normal frames and
			   check that the alarm clears within 10 seconds.  */
			if (flags & (XP_STATE_RYEL | XP_STATE_RRA)) {
				if ((sp->status.count += interval) < 10 * 8000) {
					/* wait up to 10 seconds */
					break;
				}
				/* Alarms did not clear after 10 seconds, but this might just be a sync
				   problem. */
			}
			/* start transmitter */
			/* LICR.0: TPD: Transmit Power Down. 0 = normal; 1 = power-down. */
			xlb[0x7c] &= ~(1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->config.ifflags |= (SDL_IF_TX_UP);
			sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
			sp->status.count = 0;
			state = 13;
			/* fall through */
		case 13:
			/* Stable state, normal mode, transmitters externally connected, sending normal
			   frames.  This is an active connection, not a monitoring tap. Flags have been set
			   accordingly.  Stay in this state unless we lose carrier, signal, start receiving
			   unframed all ones, or lose sync for more than 400 ms. */
			/* Stable state, monitoring mode, transmitters externally disconnected, sending
			   unframed all ones.  This is a monitoring tap (active or passive).  Flags have been 
			   set accordingly.  Stay in this state unless we lose carrier, signal, start
			   receiving unframed all ones, or lose sync for more than 400 ms. */
			if (flags & (XP_STATE_RLOS)) {
				/* lost sync */
				if ((sp->status.count += interval) >= 3200) {
					/* send unframed all ones */
					/* TCR1.1=0: TBL: Transmit Blue Alarm. 0 = normal; 1 = tx unframed
					   all ones. */
					xlb[0x35] |= (1 << 1);
					sp->status.count = 0;
					state = 10;
					continue;
				}
				/* wait for up to 400 ms */
				break;
			}
			sp->status.count = 0;
			/* stay in this state */
			break;
		}
		break;
	}
	/* set the new state variable */
	sp->status.state = state;
}

/** xp_t1_change_config: - try changing span configuration for sync
  * @sp: span structure pointer
  * @xlb: span iobase
  *
  * Diagnose the problem of not acheiving frame synchronization and attempt to change the
  * configuration accordingly.  This is a T1 line so likely difficulties are ESF framing instead of
  * D4 framing; CRC6 instead of CRC6J; B8ZS instead of AMI.  We should simply walk through the
  * possible configurations until one that works is found or all are exhausted.
  */
noinline __unlikely int
xp_t1_change_config(struct sp *sp, register volatile uint8_t *xlb)
{
	switch (sp->status.config) {
	case 0:		/* ESF, B8ZS, CRC6. */
		if (!ansi || japan) {
			sp->config.ifgtype = SDL_GTYPE_J1;
			if (sp->config.ifframing != SDL_FRAMING_ESF) {
				sp->config.ifframing = SDL_FRAMING_ESF;
				xlb[0x38] |= (1 << 7);	/* CCR2.7: 1 = Tx ESF */
				xlb[0x38] |= (1 << 3);	/* CCR2.3: 1 = Rx ESF */
				xlb[0x38] &= ~(1 << 5);	/* CCR2.5: 0 = Tx no SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_B8ZS) {
				sp->config.ifcoding = SDL_CODING_B8ZS;
				xlb[0x38] |= (1 << 6);	/* CCR2.6: 1 = Tx B8ZS */
				xlb[0x38] |= (1 << 2);	/* CCR2.2: 1 = Rx B8ZS */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				xlb[0x19] |= (1 << 7);	/* CCR5.7: 1 = Tx CRC6J */
				xlb[0x1e] |= (1 << 7);	/* CCR6.7: 1 = Rx CRC6J */
				xlb[0x36] |= (1 << 1);	/* TCR2.1: 1 = fr 12 */
				xlb[0x2c] |= (1 << 2);	/* RCR2.2: 1 = fr 12 */
			}
			sp->status.config = 1;
			break;
		}
		/* fall through */
	case 1:		/* ESF, B8ZS, CRC6J. */
		if (ansi || !japan) {
			sp->config.ifgtype = SDL_GTYPE_T1;
			if (sp->config.ifframing != SDL_FRAMING_ESF) {
				sp->config.ifframing = SDL_FRAMING_ESF;
				xlb[0x38] |= (1 << 7);	/* CCR2.7: 1 = Tx ESF */
				xlb[0x38] |= (1 << 3);	/* CCR2.3: 1 = Rx ESF */
				xlb[0x38] &= ~(1 << 5);	/* CCR2.5: 0 = Tx no SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x38] &= ~(1 << 6);	/* CCR2.6: 0 = Tx AMI */
				xlb[0x38] &= ~(1 << 2);	/* CCR2.2: 0 = Rx AMI */
				xlb[0x73] = 0x1c;	/* Set FDL reg. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
				sp->config.ifgcrc = SDL_GCRC_CRC6;
				xlb[0x19] &= ~(1 << 7);	/* CCR5.7: 1 = Tx CRC6 */
				xlb[0x1e] &= ~(1 << 7);	/* CCR6.7: 1 = Rx CRC6 */
				xlb[0x36] &= ~(1 << 1);	/* TCR2.1: 0 = bit2 */
				xlb[0x2c] &= ~(1 << 2);	/* RCR2.2: 0 = bit2 */
			}
			sp->status.config = 2;
			break;
		}
		/* fall through */
	case 2:		/* ESF, AMI, CRC6. */
		if (!ansi || japan) {
			sp->config.ifgtype = SDL_GTYPE_J1;
			if (sp->config.ifframing != SDL_FRAMING_ESF) {
				sp->config.ifframing = SDL_FRAMING_ESF;
				xlb[0x38] |= (1 << 7);	/* CCR2.7: 1 = Tx ESF */
				xlb[0x38] |= (1 << 3);	/* CCR2.3: 1 = Rx ESF */
				xlb[0x38] &= ~(1 << 5);	/* CCR2.5: 0 = Tx no SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x38] &= ~(1 << 6);	/* CCR2.6: 0 = Tx AMI */
				xlb[0x38] &= ~(1 << 2);	/* CCR2.2: 0 = Rx AMI */
				xlb[0x73] = 0x1c;	/* Set FDL reg. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				xlb[0x19] |= (1 << 7);	/* CCR5.7: 1 = Tx CRC6J */
				xlb[0x1e] |= (1 << 7);	/* CCR6.7: 1 = Rx CRC6J */
				xlb[0x36] |= (1 << 1);	/* TCR2.1: 1 = fr 12 */
				xlb[0x2c] |= (1 << 2);	/* RCR2.2: 1 = fr 12 */
			}
			sp->status.config = 3;
			break;
		}
		/* fall through */
	case 3:		/* ESF, AMI, CRC6J. */
		if (ansi || !japan) {
			sp->config.ifgtype = SDL_GTYPE_T1;
			if (sp->config.ifframing != SDL_FRAMING_SF) {
				sp->config.ifframing = SDL_FRAMING_SF;
				xlb[0x38] &= ~(1 << 7);	/* CCR2.7: 0 = Tx D4 */
				xlb[0x38] &= ~(1 << 3);	/* CCR2.3: 0 = Rx D4 */
				xlb[0x38] |= (1 << 5);	/* CCR2.5: 1 = Tx SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_B8ZS) {
				sp->config.ifcoding = SDL_CODING_B8ZS;
				xlb[0x38] |= (1 << 6);	/* CCR2.6: 1 = Tx B8ZS */
				xlb[0x38] |= (1 << 2);	/* CCR2.2: 1 = Rx B8ZS */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
				sp->config.ifgcrc = SDL_GCRC_CRC6;
				xlb[0x19] &= ~(1 << 7);	/* CCR5.7: 1 = Tx CRC6 */
				xlb[0x1e] &= ~(1 << 7);	/* CCR6.7: 1 = Rx CRC6 */
				xlb[0x36] &= ~(1 << 1);	/* TCR2.1: 0 = bit2 */
				xlb[0x2c] &= ~(1 << 2);	/* RCR2.2: 0 = bit2 */
			}
			sp->status.config = 4;
			break;
		}
		/* fall through */
	case 4:		/* D4, B8ZS, Norm. */
		if (!ansi || japan) {
			sp->config.ifgtype = SDL_GTYPE_J1;
			if (sp->config.ifframing != SDL_FRAMING_SF) {
				sp->config.ifframing = SDL_FRAMING_SF;
				xlb[0x38] &= ~(1 << 7);	/* CCR2.7: 0 = Tx D4 */
				xlb[0x38] &= ~(1 << 3);	/* CCR2.3: 0 = Rx D4 */
				xlb[0x38] |= (1 << 5);	/* CCR2.5: 1 = Tx SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_B8ZS) {
				sp->config.ifcoding = SDL_CODING_B8ZS;
				xlb[0x38] |= (1 << 6);	/* CCR2.6: 1 = Tx B8ZS */
				xlb[0x38] |= (1 << 2);	/* CCR2.2: 1 = Rx B8ZS */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				xlb[0x19] |= (1 << 7);	/* CCR5.7: 1 = Tx CRC6J */
				xlb[0x1e] |= (1 << 7);	/* CCR6.7: 1 = Rx CRC6J */
				xlb[0x36] |= (1 << 1);	/* TCR2.1: 1 = fr 12 */
				xlb[0x2c] |= (1 << 2);	/* RCR2.2: 1 = fr 12 */
			}
			sp->status.config = 5;
			break;
		}
		/* fall through */
	case 5:		/* D4, B8ZS, D4Yel. */
		if (ansi || !japan) {
			sp->config.ifgtype = SDL_GTYPE_T1;
			if (sp->config.ifframing != SDL_FRAMING_SF) {
				sp->config.ifframing = SDL_FRAMING_SF;
				xlb[0x38] &= ~(1 << 7);	/* CCR2.7: 0 = Tx D4 */
				xlb[0x38] &= ~(1 << 3);	/* CCR2.3: 0 = Rx D4 */
				xlb[0x38] |= (1 << 5);	/* CCR2.5: 1 = Tx SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x38] &= ~(1 << 6);	/* CCR2.6: 0 = Tx AMI */
				xlb[0x38] &= ~(1 << 2);	/* CCR2.2: 0 = Rx AMI */
				xlb[0x73] = 0x1c;	/* Set FDL reg. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
				sp->config.ifgcrc = SDL_GCRC_CRC6;
				xlb[0x19] &= ~(1 << 7);	/* CCR5.7: 1 = Tx CRC6 */
				xlb[0x1e] &= ~(1 << 7);	/* CCR6.7: 1 = Rx CRC6 */
				xlb[0x36] &= ~(1 << 1);	/* TCR2.1: 0 = bit2 */
				xlb[0x2c] &= ~(1 << 2);	/* RCR2.2: 0 = bit2 */
			}
			sp->status.config = 6;
			break;
		}
		/* fall through */
	case 6:		/* D4, AMI, Norm. */
		if (!ansi || japan) {
			sp->config.ifgtype = SDL_GTYPE_J1;
			if (sp->config.ifframing != SDL_FRAMING_SF) {
				sp->config.ifframing = SDL_FRAMING_SF;
				xlb[0x38] &= ~(1 << 7);	/* CCR2.7: 0 = Tx D4 */
				xlb[0x38] &= ~(1 << 3);	/* CCR2.3: 0 = Rx D4 */
				xlb[0x38] |= (1 << 5);	/* CCR2.5: 1 = Tx SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x38] &= ~(1 << 6);	/* CCR2.6: 0 = Tx AMI */
				xlb[0x38] &= ~(1 << 2);	/* CCR2.2: 0 = Rx AMI */
				xlb[0x73] = 0x1c;	/* Set FDL reg. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				xlb[0x19] |= (1 << 7);	/* CCR5.7: 1 = Tx CRC6J */
				xlb[0x1e] |= (1 << 7);	/* CCR6.7: 1 = Rx CRC6J */
				xlb[0x36] |= (1 << 1);	/* TCR2.1: 1 = fr 12 */
				xlb[0x2c] |= (1 << 2);	/* RCR2.2: 1 = fr 12 */
			}
			sp->status.config = 7;
			break;
		}
		/* fall through */
	case 7:		/* D4, AMI, D4Yel. */
		/* last configuration failed, go back to default */
		if (ansi || !japan) {
			sp->config.ifgtype = SDL_GTYPE_T1;
			if (sp->config.ifframing != SDL_FRAMING_ESF) {
				sp->config.ifframing = SDL_FRAMING_ESF;
				xlb[0x38] |= (1 << 7);	/* CCR2.7: 1 = Tx ESF */
				xlb[0x38] |= (1 << 3);	/* CCR2.3: 1 = Rx ESF */
				xlb[0x38] &= ~(1 << 5);	/* CCR2.5: 0 = Tx no SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_B8ZS) {
				sp->config.ifcoding = SDL_CODING_B8ZS;
				xlb[0x38] |= (1 << 6);	/* CCR2.6: 1 = Tx B8ZS */
				xlb[0x38] |= (1 << 2);	/* CCR2.2: 1 = Rx B8ZS */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
				sp->config.ifgcrc = SDL_GCRC_CRC6;
				xlb[0x19] &= ~(1 << 7);	/* CCR5.7: 1 = Tx CRC6 */
				xlb[0x1e] &= ~(1 << 7);	/* CCR6.7: 1 = Rx CRC6 */
				xlb[0x36] &= ~(1 << 1);	/* TCR2.1: 0 = bit2 */
				xlb[0x2c] &= ~(1 << 2);	/* RCR2.2: 0 = bit2 */
			}
			sp->status.config = 0;
		} else {
			sp->config.ifgtype = SDL_GTYPE_J1;
			if (sp->config.ifframing != SDL_FRAMING_ESF) {
				sp->config.ifframing = SDL_FRAMING_ESF;
				xlb[0x38] |= (1 << 7);	/* CCR2.7: 1 = Tx ESF */
				xlb[0x38] |= (1 << 3);	/* CCR2.3: 1 = Rx ESF */
				xlb[0x38] &= ~(1 << 5);	/* CCR2.5: 0 = Tx no SLC-96 */
			}
			if (sp->config.ifcoding != SDL_CODING_B8ZS) {
				sp->config.ifcoding = SDL_CODING_B8ZS;
				xlb[0x38] |= (1 << 6);	/* CCR2.6: 1 = Tx B8ZS */
				xlb[0x38] |= (1 << 2);	/* CCR2.2: 1 = Rx B8ZS */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
				sp->config.ifgcrc = SDL_GCRC_CRC6J;
				xlb[0x19] |= (1 << 7);	/* CCR5.7: 1 = Tx CRC6J */
				xlb[0x1e] |= (1 << 7);	/* CCR6.7: 1 = Rx CRC6J */
				xlb[0x36] |= (1 << 1);	/* TCR2.1: 1 = fr 12 */
				xlb[0x2c] |= (1 << 2);	/* RCR2.2: 1 = fr 12 */
			}
			sp->status.config = 1;
		}
		return (1);
	}
	return (0);
}

/** xp_t1_process_stats: - process statistics (performance measures) for the span
  * @sp: span structure pointer
  * @errors: error events accumulated so far.
  *
  * ERROR EVENTS:
  * Bipolar Violation BPV Error Event: A BPV error event for an AMI coded signal is the occurrence
  *	of a pulse of the same polarity as the previous pulse.  A BPV error event for a B8ZS or HDB3
  *	coded signal is the occurrence of a pulse of the same polarity as the previous pulse without
  *	being part of the zero substitution code. 
  *
  * Controlled Slip (CS) Error Event: A CS is the replication or deletion of the payload bits of a
  *	DS1 frame.  A CS may be performed when there is a difference between the timing of a
  *	synchronous receiving terminal an the received signal.  A CS does not acuase an Out of Frame
  *	defect. 
  *
  * Excessive Zeros (EXZ) Error Event: An EXZ error event for an AMI coded signal is the occurence
  *	of more than fifteen contiguous zeros.  For a B8ZS coded signal, the defect occurs when more
  *	than seven contiguous zeros are detected. 
  *
  * Line Coding Violation (LCV) Error Event: An LCV is the occurence of either a Bipolar Violation
  *	or Excessive Zeros (EXZ) error event. 
  *
  * Path Coding Violation (PCV) Error Event: A PCV error event is a frame synchronization bit error
  *	in the D4 and E1-non-CRC-4 formats, or a CRC error in the ESF and E1-CRC-4 formats. 
  *
  * PERFORMANCE PARAMETERS:
  * All performance parameters are accumulated in fifteen minute intervals and up to 96 intervals
  * (covering a 24-hour period) are kept by an agent.  Fewer than 96 intervals of data will be
  * available if the agent has been restarted within the last 24 hours.  In addition, there is a
  * rolling 24-hour total of each performance parameter.  There is no requirement for an agent to
  * ensure a fixed relationship between the start of a fifteen minute interval and clock time;
  * however, some agents may align the fifteen minute intervals with quarter hours. 
  *
  * Severely Errored Seconds (SES): An SES for ESF signals is a second with one of the following:
  *	320 or more PCV error events; one or more OOF defects; a detected AIS defect. For E1 CRC-4
  *	signals, an SES is a second with either 832 or more PCV error events or one or more OOF
  *	defects.  For E1 non-CRC-4 signals, an SES is a 2048 (LCV ????) PCV or more. For D4 signals,
  *	an SES is a count of one-second intervals with Framing Error events, or an OOF defect, or
  *	1544 LCV or more.  Controlled slips are not included in this parameter. This is not
  *	incremented during an Unavailable Second.
  *
  * Severely Errored Framing Seconds (SEFS): An SEFS is a second with either one or more OOF
  *	defects, or a detected AIS defect.
  *
  * Unavailable Seconds (UASs):  A performance management event that is calculted by counting the
  *	number of seconds for which the interface is unavailable.  An interface is said to be
  *	unavailable from the onset of 10 continugous Severely Errored Seconds (SESs), or on the
  *	onset of a condition leading to a failutre. Once unavailable, and if no failure is present,
  *	an interface becomes available at the onset of 10 contiguous seconds with no SESs. 
  *
  *	UAS are calculated by counting the number of seconds that the interface is unavailable.  The
  *	DS1 interface is said to be unavailable from the onset of ten contiguous SESs, or the onset
  *	of the condition leadeing to a failure (see Failure States).  If the condition leading to
  *	the failure was immediately preceded by one or more continguous SES, then the DS1 interface
  *	unavailability starts from the onset of these SES. Once unavailable, and if no failure is
  *	present, the DS1 interfae becomes available at the onset of 10 contiguous seconds with no
  *	SES.  if the failure clearing time is less than or equat to ten seconds.  If the failure
  *	clearing time is more than ten seconds, the DS1 interface becoms available at the onset of
  *	tem contiguous seconds with no SES, or the onset period leading to the successful clearing
  *	condition, whichever occurs later.  With respect to DS1 error counts, all coutners are
  *	incremented while the DS1 interface is deemed available.  While the interface is deemed
  *	unavailable, the only count that is incremented is UAS.  A special case exists when the ten
  *	or more second period crosses the 900 second statistic window boundary, as the foregoing
  *	description ipmlies that the SES and UAS counter must be adjusted when the UAS stats is
  *	entered.  Successive "gets" of the affectetd dsx1IntervalSESs and dsx1IntervalUASs objects
  *	will return differing values if the first get occurs during the first few seconds of the
  *	window.  This is viewed as an unavoidable side-effect of selecting the presently-defined
  *	objects.  
  *
  * Bursty Errored Secods (BES): A BES (also known as an Errored Second Type B) is a second with
  *	fewer than 320 and more than one PCV, no SEF defects, and no detected incoming AIS defects.
  *	Controlled slips are not incldued in this parameter. 
  *
  * Errored Seconds (ES): For ESF and E1-CRC links an Errored Second is a second with one of the
  *	followng: one or more PCV; one or more OOF defects; one or more Controlled Slip events; a
  *	detected AIS defect.
  *
  * Controlled Slip Seconds (CSS): A cCSS is a one-second interval containing one or more controlled
  *	slips.
  *
  * Line Errored Seconds (LES): A LES, according to T1M1.3, is a second in which one or more LCV
  *	error events were detected.  While many implementations are currently unable to detect the
  *	zero strings, it is expected that interface manufacturers will add this capability in
  *	deference to ANSI; therefore, it will become available in time. In the T1M1.3 specification,
  *	near end LCV and far end LES are counted.  For consistency, we count LES at both ends.
  *
  * Degraded Minutes: A DM is one in which the estimated error rate exceeds 1E-06 but does not
  *	exceed 1E-03.  DM are determined by collecting all of the Available Seconds, removing any
  *	Severely Errored Seconds grouping the result in 60-second long groups and counting a
  *	60-second long group (minute) as degraded if the cumulative errors during the seconds
  *	present in the group exceed 1E-6.  Available seconds are merely those seconds which are not
  *	unavailable as described below. 
  */
static noinline fastcall __hot void
xp_t1_process_stats(struct sp *sp, register uint errors)
{
	/* This is a one second timeout */

	/* Calculate severely errored seconds. */
	/* Note: when there are a lot of PCV and the span is T1, there is possibility that it should be set
	   to J1 instead.  When there are a lot of PCV and the span is J1, there is a possibility that it
	   should be set to T1 instead. */
	switch (sp->config.ifframing) {
	case SDL_FRAMING_ESF:
		/* A Severely Errored Second for ESF signals is a second with one of the following: 320 or
		   more PCV error events; one or more OOF defects; or a detected AIS defect. */
		if (sp->stats[0].PCVs >= 320)
			errors |= XP_ERR_SES;
		break;
	case SDL_FRAMING_D4:
		/* For D4 signals, an SES is a count of one-second intervals with Framing Error events, or an 
		   Out of Frame defect; or 1544 or more LCVs. */
		if (sp->stats[0].LCVs >= 1544)
			errors |= XP_ERR_SES;
		break;
	}

	/* The number of Unavailable Seconds (UASs) in the current interval. */
	/* Track the number of consecutive severely errored seconds and the number of consecutive
	   non-severely errored seconds */
	if (errors & (XP_ERR_SES)) {
		sp->status.nses = 0;
		sp->status.sess++;
		if (sp->status.sess >= 10) {
			if (sp->status.sess == 10 && !(errors & XP_ERR_UAS)) {
				sp->stats[0].UASs += 9;
				errors |= XP_ERR_UAS;
			}
			errors |= XP_ERR_UAS;
		}
	} else {
		sp->status.sess = 0;
		sp->status.nses++;
		if (sp->status.nses >= 10) {
			if (sp->status.nses == 10 && (errors & XP_ERR_UAS)) {
				sp->stats[0].UASs -= 9;
				errors &= ~XP_ERR_UAS;
			}
			errors &= ~XP_ERR_UAS;
		}
	}
	if (errors & (XP_ERR_UAS))
		sp->stats[0].UASs = 1;

	/* Calculate bursty errored seconds */
	switch (sp->config.ifframing) {
	case SDL_FRAMING_ESF:
		if (1 < sp->stats[0].PCVs && sp->stats[0].PCVs < 320)
			if (!(errors & XP_ERR_SEFS))
				if (!(errors & XP_FAIL_AIS))
					errors |= XP_ERR_BES;
		break;
	case SDL_FRAMING_D4:
		if (1 < sp->stats[0].LCVs && sp->stats[0].LCVs < 1544)
			if (!(errors & XP_ERR_SEFS))
				if (!(errors & XP_FAIL_AIS))
					errors |= XP_ERR_BES;
		break;
	}

	/* The number of Errored Seconds (ESs) in the current interval. */
	if (sp->stats[0].PCVs > 0)
		errors |= XP_ERR_ES;
	if (errors & (XP_ERR_ES))
		sp->stats[0].ESs = 1;
	/* The number of Severely Errored Seconds (SESs) in the current interval. */
	if (errors & (XP_ERR_SES))
		sp->stats[0].SESs = 1;
	/* The number of Severely Errored Framing Seconds (SEFSs) in the current interval. */
	if (errors & (XP_ERR_SEFS))
		sp->stats[0].SEFSs = 1;
	/* The number of Controlled Slip Seconds (CSSs) in the current interval. */
	if (errors & (XP_ERR_CSS))
		sp->stats[0].CSSs = 1;
	/* The number of Line Errored Seconds (LESs) in the current interval. */
	if (errors & (XP_ERR_LES))
		sp->stats[0].LESs = 1;
	/* The number of Bursty Errored Seconds (BESs) in the current interval. */
	if (errors & (XP_ERR_BES))
		sp->stats[0].BESs = 1;

	/* accumulate the one-second counts */
	sp->stats[1].ESs += sp->stats[0].ESs;
	sp->stats[1].SESs += sp->stats[0].SESs;
	sp->stats[1].ESs += sp->stats[0].ESs;
	sp->stats[1].SESs += sp->stats[0].SESs;
	sp->stats[1].SEFSs += sp->stats[0].SEFSs;
	sp->stats[1].UASs += sp->stats[0].UASs;
	sp->stats[1].CSSs += sp->stats[0].CSSs;
	sp->stats[1].PCVs += sp->stats[0].PCVs;
	sp->stats[1].LESs += sp->stats[0].LESs;
	sp->stats[1].BESs += sp->stats[0].BESs;
	sp->stats[1].DMs += sp->stats[0].DMs;
	sp->stats[1].LCVs += sp->stats[0].LCVs;
	sp->stats[1].FASEs += sp->stats[0].FASEs;
	sp->stats[1].FABEs += sp->stats[0].FABEs;
	sp->stats[1].FEBEs += sp->stats[0].FEBEs;
	bzero(&sp->stats[0], sizeof(sp->stats[0]));

	if ((++sp->stats[1].SECs) == 60) {
		/* The number of Degraded Minutes (DMs) in the current interval. */
		if (errors & (XP_ERR_DM))
			sp->stats[1].DMs = 1;

		/* accumulate one-minute counts */
		sp->stats[2].ESs += sp->stats[1].ESs;
		sp->stats[2].SESs += sp->stats[1].SESs;
		sp->stats[2].ESs += sp->stats[1].ESs;
		sp->stats[2].SESs += sp->stats[1].SESs;
		sp->stats[2].SEFSs += sp->stats[1].SEFSs;
		sp->stats[2].UASs += sp->stats[1].UASs;
		sp->stats[2].CSSs += sp->stats[1].CSSs;
		sp->stats[2].PCVs += sp->stats[1].PCVs;
		sp->stats[2].LESs += sp->stats[1].LESs;
		sp->stats[2].BESs += sp->stats[1].BESs;
		sp->stats[2].DMs += sp->stats[1].DMs;
		sp->stats[2].LCVs += sp->stats[1].LCVs;
		sp->stats[2].FASEs += sp->stats[1].FASEs;
		sp->stats[2].FABEs += sp->stats[1].FABEs;
		sp->stats[2].FEBEs += sp->stats[1].FEBEs;
		bzero(&sp->stats[1], sizeof(sp->stats[1]));

		if ((sp->stats[2].SECs += 60) == 900) {
			uint index = sp->curr;

			sp->hist[index] = sp->stats[2];
			sp->hist[index].ValidData = 2;	/* true(2) */
			bzero(&sp->stats[2], sizeof(sp->stats[2]));
			if (++index == 96)
				index = 0;
			sp->curr = index;
		}
	}
	return;
}

/** xp_t1_process_alarms: - process alarm state and das blinkin' ligths
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span register i/o pointer
  * @flags: state flags
  * @timeout: true when interrupt is a timeout (for alarm recovery)
  */
static noinline fastcall __hot void
xp_t1_process_alarms(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint flags,
		     uint timeout)
{
	register uint alarms = sp->config.ifalarms;

	/* integrate alarms */
	if ((flags & XP_STATE_RDMA) && (sp->config.ifframing == SDL_FRAMING_CAS))
		alarms |= SDL_ALARM_DMF;
	else
		alarms &= ~SDL_ALARM_DMF;
	if (flags & (XP_STATE_RRA | XP_STATE_RYEL))
		alarms |= SDL_ALARM_YEL;
	else
		alarms &= ~SDL_ALARM_YEL;
	if (flags & XP_STATE_RUA1)
		alarms |= SDL_ALARM_BLU;
	else
		alarms &= ~SDL_ALARM_BLU;
	if (flags & (XP_STATE_RLOS | XP_STATE_FRCL | XP_STATE_LRCL))
		alarms |= SDL_ALARM_RED;
	else
		alarms &= ~SDL_ALARM_RED;
	if ((alarms & SDL_ALARM_REC) && (timeout)) {
		if (alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
			alarms &= ~SDL_ALARM_REC;
		else {
			/* recovery time in terms of timeouts (5 seconds) */
			if (sp->recovertime && !--sp->recovertime) {
				xlb[0x35] &= ~(1 << 0);	/* TCR1.0: TYEL: 0 = no YEL */
				alarms &= ~SDL_ALARM_REC;
				cd->eval_syncsrc = 1;
			}
		}
	}
	/* if any of the alarms (or recovery) changed we need to do this */
	if ((alarms ^ sp->config.ifalarms)
	    & (SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC)) {
		if ((alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
		    && !(sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) {
			/* Local alarms have just begun.  Signal a yellow (RAI) alarm to the other end. */
			if (!(alarms & SDL_ALARM_REC)) {
				/* local alarms just begun and we were not in recovery */
				xlb[0x35] |= (1 << 0);	/* TCR1.0: TYEL: 1 = transmit yellow alarm */
				if (alarms & SDL_ALARM_RED)
					cd->eval_syncsrc = 1;
			} else
				alarms &= ~SDL_ALARM_REC;
		} else if ((alarms & SDL_ALARM_RED)
			   && !(sp->config.ifalarms & SDL_ALARM_RED)) {
			/* red alarm just got added to the set, reevaluate the sync source */
			cd->eval_syncsrc = 1;
		} else if (!(alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
			   && (sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) {
			/* Local alarms have just ended. */
			alarms |= SDL_ALARM_REC;
			if (xlb[0x30] & (1 << 2))	/* CCR3.2: ECUS */
				sp->recovertime = X400P_SDL_ALARM_SETTLE_T1;
			else
				sp->recovertime = X400P_SDL_ALARM_SETTLE_SECONDS;
		}
		sp->config.ifalarms &= ~(SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC);
		sp->config.ifalarms |=
		    (alarms & (SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC));

		{
			int leds = 0, all_leds;

			/* adjust leds */
			if (alarms & SDL_ALARM_RED)
				leds |= LEDRED;
			else if (alarms & SDL_ALARM_YEL)
				leds |= LEDYEL;
			else
				leds |= LEDGRN;
			all_leds = cd->ledreg;
			all_leds &= ~(LEDYEL << (sp->span << 1));
			all_leds |= (leds << (sp->span << 1));
			if (cd->ledreg != all_leds) {
				cd->ledreg = all_leds;
				cd->xlb[LEDREG] = cd->ledreg;
			}
		}
	}
}

/** xp_t1_eval_syncsrc: - reevaluate synchronization source for card
  * @cd: card structure pointer
  *
  * There is really only one version of this function, it is just laid out in several places for
  * proper position with respect to the main interrupt service routine.
  */
static noinline fastcall __hot void
xp_t1_eval_syncsrc(struct cd *cd)
{
	/* for safety */
	if (cd->config.ifsyncsrc[0] != 0) {
		int src, synreg = SYNCSELF, ctlreg, clkreg = cd->clkreg;

		for (src = 0; src < SDL_SYNCS; src++) {
			struct sp *sp;
			uint span;

			if ((span = cd->config.ifsyncsrc[src]) == 0)
				break;
			if ((--span < cd->ports && span >= 0)
			    && (sp = cd->spans[span])
			    && (sp->config.ifflags & SDL_IF_UP)
			    && (sp->config.ifflags & SDL_IF_RX_UP)
			    && (sp->config.ifflags & SDL_IF_RX_RUNNING)
			    && (sp->config.ifclock != SDL_CLOCK_LOOP)
			    && !(sp->config.ifalarms & (SDL_ALARM_BLU | SDL_ALARM_RED))) {
				synreg = cd->config.ifsyncsrc[src];
				if (sp->config.ifgtype == SDL_GTYPE_E1)
					clkreg |= E1DIV;
				else
					clkreg &= ~E1DIV;
				break;
			}
		}
		if (cd->config.ifsync != synreg) {
			cd->config.ifsync = synreg;
			ctlreg = cd->ctlreg & ~E1DIV;
			ctlreg |= clkreg;	/* use divider for span or clock */
			cd->xlb[CTLREG] = cd->ctlreg = ctlreg;
			cd->xlb[SYNREG] = cd->synreg = synreg;
		}
	}
}

static noinline fastcall __hot void xp_e1_txrx_burst(struct cd *cd);
static noinline fastcall __hot void xp_e1_process_span(struct cd *cd, struct sp *sp);
static noinline fastcall __hot void xp_e1_process_timeout(struct cd *cd, struct sp *sp, volatile uint8_t *xlb,
							  register uint flags);
static noinline fastcall __hot void xp_e1_process_state(struct cd *cd, struct sp *sp,
							register volatile uint8_t *xlb,
							const register uint flags,
							const register uint errors);
static noinline fastcall __hot void xp_e1_process_stats(struct sp *sp, register uint errors);
static noinline fastcall __hot void xp_e1_process_alarms(struct cd *cd, struct sp *sp,
							 register volatile uint8_t *xlb, register uint flags,
							 uint timeout);
static noinline fastcall __hot void xp_e1_eval_syncsrc(struct cd *cd);
static noinline fastcall __unlikely void xp_e1_process_auto(struct cd *cd, struct sp *sp,
							    register volatile uint8_t *xlb,
							    register uint flags, const register uint errors,
							    const uint timeout);
noinline __unlikely int xp_e1_change_config(struct sp *sp, register volatile uint8_t *xlb);

/** xp_e1_interrupt: - E400P-SS7 Interrupt Service Routine
  * @irq: interrupt number
  * @dev_id: card structure pointer (opaque)
  * @regs: interrupted registers
  *
  * The user will always preceed a read of any fo the SR1, SR2, and RIR registers with a write.  The
  * user will write a byte to one of these registers, with a one in the bit positions he or she
  * wishes to read and a zero in the bit positions he or she does not wish to obtain the latest
  * information on.  When a one is written to a bit location, the read register will be updated with
  * the latest information.  When a zero is written to a bit position, the read register will not be
  * updated and the previous value will be held.  A write to the status and information registers
  * will be immediated followed by a read of the same register.  The read result should be logically
  * ANDed with the mask byte that was just written and this value should be written back to the same
  * register to insure that the bit does indeed clear.  This second write step is necessary because
  * the alarms and events in the status registers occur asyncrhonously in respect to their access
  * via the parallel port.  This write-read-write scheme allows an external controller or
  * microprocessor to individually poll certain bits without disturbing the other bits in the
  * register.  This operation is key in controlling the DS21354/DS21554 with higher-order software
  * languages.
  *
  * Process an interrupt for a E400P card (with a DS21354/DS21554 chip).  The spans on this card can
  * only be E1 span.  This is a departure from the previous driver which counted frames to determine
  * timeout events: here we utilize the Dallas interrupts available on the card for non-frame
  * events.  This function should really be aligned on a page boundary and the functions that it
  * calls laid out immediately following the function, thus the forward declarations above.
  */
STATIC __hot irqreturn_t
#ifdef HAVE_KTYPE_IRQ_HANDLER_2ARGS
xp_e1_interrupt(int irq, void *dev_id)
#else
xp_e1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
	static unsigned long lasttime = 0;
	struct cd *cd = (struct cd *) dev_id;
	int safety;

	/* Keep another processor from entering the interrupt while another is already processing the
	   interrupt.  This means that the top half of the driver must disable interrupts when taking the
	   card lock to avoid deadly embrace. */
	spin_lock(&cd->lock);
	/* active interrupt (otherwise spurious or shared) */
	if (!(cd->xlb[STAREG] & (INTACTIVE | DINTACT))) {
		spin_unlock(&cd->lock);
		return (irqreturn_t) (IRQ_NONE);
	}
	if (likely(cd->xlb[STAREG] & INTACTIVE)) {
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | OUTBIT | DINTENA | INTACK);
		xp_e1_txrx_burst(cd);
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | DINTENA);
	}
	for (safety = 0; unlikely(cd->xlb[STAREG] & DINTACT); safety++) {
		uint span;
		struct sp *sp;

		/* We would like to try to keep this burning a loop here when there is only one span that is
		   responsible for the Dallas interrupt; however, the DS21354/554 chips do not support an
		   interrupt information register like the DS2155/455/458. */
		for (span = 0; span < cd->ports; span++)
			if (likely((sp = cd->spans[span]) != NULL))
				xp_e1_process_span(cd, sp);

		if (unlikely(safety > 8)) {
			/* throttle software error message */
			if (lasttime == 0 || (jiffies - lasttime) > HZ) {
				lasttime = jiffies;
				swerr();
			}
			break;
		}
	}
	/* Reevaluate the sync source when necessary.  Try to skip the call here whenever possible to keep
	   the ISR tight. */
	if (unlikely(xchg((int *) &cd->eval_syncsrc, 0) != 0)) {
		if (cd->config.ifsyncsrc[0] != 0)
			xp_e1_eval_syncsrc(cd);
	}
	spin_unlock(&cd->lock);
	return (irqreturn_t) (IRQ_HANDLED);
}

/** xp_e1_txrx_burst: - burst transfer TX and RX data
  * @cd: card structure pointer
  *
  * This function is responsible for transfering frame data to and from the card.  This is being
  * done with host driven I/O because the PLX9030 is incapable of bus mastering DMA.  When the
  * memory mapped I/O region is properly described, PCI burst transfers will occur.  Transfers are
  * 1024 bytes written and 1024 bytes read.  These are interleaved, but might better be performed as
  * 4 Lword interleaved transfers.
  *
  * This function is identical to xp_x1_txrx_burst(), but is laid out again here so that it is in
  * the correct location w.r.t. the main interrupt routine.
  */
static noinline fastcall __hot void
xp_e1_txrx_burst(struct cd *cd)
{
	int lebno;

	if ((lebno = (cd->lebno + 1) & (X400P_EBUFNO - 1)) != cd->uebno) {
		register int slot;
		register volatile uint32_t *xll;
		register const uint32_t *wbuf = cd->wbuf + (lebno << 8);
		register const uint32_t *const wend = wbuf + 256;
		register uint32_t *rbuf = cd->rbuf + (lebno << 8);

		cd->lebno = lebno;
		for (xll = cd->xll; wbuf < wend;)
			for (wbuf++, rbuf++, xll++, slot = 1; slot < 32; slot++, xll++, wbuf++, rbuf++) {
				prefetch(wbuf + 1);
				prefetchw(rbuf + 1);
				*xll = *wbuf;
				*rbuf = *xll;
			}
		tasklet_hi_schedule(&cd->tasklet);
	} else
		xp_overflow(cd);
}

/** xp_e1_process_span: - process Dallas interrupts for a single span
  * @cd: card structure pointer
  * @sp: span structure pointer
  *
  * PERFORMANCE DEFECTS:
  *
  * XP_DEF_AIS: Alarm Indication Signal (AIS) Defect:
  *	For D4 and ESF links, the 'all ones' condition is detected at a DS1 line interface upon
  *	observing an unframed signal with a one's density of at least 99.9 percent present for a
  *	time equal to or greater than T, where 3ms is less than or equal to T, which is less than or
  *	equal to 75ms.  The AIS is terminated upon observing a signal not meeting the one's density
  *	of the unframed signal criteria for a period equal to or greater than T.  For E1 links, the
  *	'all-ones' condition is detected at the line interface as a string of 512 bits containing
  *	fewer than three zero bits. 
  *
  * XP_DEF_OOF: Out of Frame (OOF) Defect:
  *	An OOF defect is the occurrence of a particular density of Framing Error events.  For T1
  *	links, an OOF defect is declared when the receiver detects two or more framing errors within
  *	a 3 ms period for ESF signals and 0.75 ms for D4 signals, or two or more errors out of five,
  *	or fewer consecutive framing-bits.  For E1 links, an OOF defect is declared when three
  *	consecutive frame alignment signals have been received with an error.  When an OOF defect is
  *	declared, the frame starts searching for a correct pattern.  The OOF defect ends when the
  *	signal is in-frame.  In-frame occurs when there are fewer than two frame bit errors within a
  *	3 ms period for ESF signals and 0.75 ms for D4 signals.  For E1 links, in-frame occurs when
  *	in frame N, the frame alignment signal is correct; and, in frame N+1, the frame alignment
  *	signal is absent (that is, bit 2 in TS0 is set to one); and, in frame N+2, the frame
  *	alignment signal is present and correct. 
  *
  * FAILURE STATES: The following failure states are received or detected failures that are
  * reported.  The conditions under which a DS1 interface would, if ever, produce the conditions
  * leading to the failure state are described in the appropriate specification.
  *
  * XP_FAIL_AIS: Alarm Indication Signal (AIS) Failure:
  *	The AIS failure is declared when an AIS defect is detected at the input and the AIS defect
  *	still exists after the LOF failure (which is caused by the unframed nature of the all-ones
  *	signal) is detected.  The AIS failure is cleared when the LOF failure is cleared.  
  *
  * XP_FAIL_FEA: Far End Alarm Failure (Yellow Alarm):
  *	The Far End Alarm failure is also known as Yellow Alarm in the T1 cases and Distant Alarm
  *	(or RAI) in the E1 case.  For D4 links, the FEA failure is declared when bit 6 of all
  *	channels have been zero for at least 335 ms and is cleared when bit 6 of at least one
  *	channel is non-zero for a period T, where T is usually less than one second and alway less
  *	than five seconds.  The FEA failure is not declared for D4 links when LOS is detected.  For
  *	ESF links, the FEA failure is declared if the Yellow Alarm signal pattern occurs in a least
  *	seven out of ten contiguous 16-bit-pattern intervals and is cleared if the Yellow Alarm
  *	signal pattern does not occur in ten contiguous 16-bit signal pattern intervals.  For E1
  *	links, the FEA failure is declared when bit 3 of time-slot zero is set to one on two
  *	consecutive occasions.  The FEA failure is cleared when bit 3 of time-slot zero is received
  *	set to zero.  
  *
  * XP_FAIL_FELOM: Far End Loss of Multiframe Failure:
  *	The FELOM failure is declared when bit 2 of TS16 of frame 0 is received set to one on two
  *	consecutive occasions.  The FELOM failure is cleared when bit 2 of TS16 of frame 0 is
  *	received set to zero.  The FELOM failure can only be declared for E1 links operating in CAS
  *	mode.  
  *
  * XP_FAIL_LPF: Loopback Pseudo-Failure:
  *	The LPF is declared when the near end equipment has placed a loop-back (of any kind) on the
  *	DS1.  This allows a management entity to determine from one object whether the DS1 can be
  *	considered to be in service or not (from the point of view of the near-end equipment).  
  *
  * XP_FAIL_LOF: Loss of Frame (LOF) Failure:
  *	For T1 links, the LOF failure is declared with an OOF or LOS defect has persisted for T
  *	seconds, where T is greater than or equal to two, but less than or equal to ten.  The LOF
  *	failure is cleared when there have been no OOF or LOS defects during a period T is greater
  *	than or equal to two, but less than or equal to twenty.  Many systems will perform "hit
  *	integration" with the period T before declaring or clearing the failure. 
  *
  * XP_FAIL_LOM: Loss of Multiframe (LOM) Failure:
  *	The LOM failure is declared when two consecutive multi-frame alignment signals (bit 4
  *	through 7 of TS16 of frame 0) have been received wtih an error.  The LOM failure is cleared
  *	when the first correct multi-frame alignment signal is received.  The LOM failure can only
  *	be declared for E1 links operating with framing (sometimes called CAS mode).  
  *
  * XP_FAIL_LOS: Loss of Signal (LOS) Failure:
  *	For T1, the Loss of Signal failure is declared upon observing 175 +/- 74 contiguous pulse
  *	position with no pulses of either positive or negative polarity.  The LOS failure is
  *	cleared upon observing an average pulse density of at least 12.5 percent over a period of
  *	175 +/- 74 contiguous pulse positions starting with the receipt of a pulse.  
  *
  * XP_FAIL_T16AIS: T16 Alarm Indication Signal Failure:
  *	For E1 links, the Ts16 Alarm Indication Signal failure is declared when time-slot 16 is
  *	received as all ones for all frames of two consecutive multi-frames.  This condition
  *	is never declared for T1.  
  */
static noinline fastcall __hot void
xp_e1_process_span(struct cd *cd, struct sp *sp)
{
	register volatile uint8_t *xlb = (typeof(xlb)) sp->iobase;
	register uint8_t status, mask;
	register uint errors = sp->status.errors;
	register uint flags = sp->status.flags;
	uint timeout = 0;

	if ((mask = xlb[0x16])) {	/* IMR1 */
		/* write-read-write cycle */
		xlb[0x06] = mask;	/* SR1 */
		status = xlb[0x06] & mask;
		xlb[0x06] = status;

		/* SR1.7: RSA1: Receive Signalling All Ones/Signalling Change.  Set when the contents of time 
		   slot 16 contains less than three zeros over 16 consecutive frames.  This alarm is not
		   disabled in the CCS signalling mode.  Both RSA1 and RSA0 will be set if a change in
		   signalling is detected. */
		/* SR1.5: RSA0: Receiv Signalling All Zeros/Signalling Change.  Set when over a full MF, time 
		   slot 16 contains all zeros.  Both RSA1 and RSA0 will be set if a change in signalling is
		   detected. */
		if ((mask & (1 << 5)) && (mask & (1 << 7))) {
			switch (status & ((1 << 5) | (1 << 7))) {
			case (1 << 5):
				flags |= XP_STATE_RSAZ;
				flags &= ~XP_STATE_RSAO;
				break;
			case (1 << 7):
				flags |= XP_STATE_RSAO;
				flags &= ~XP_STATE_RSAZ;
				break;
			case ((1 << 5) | (1 << 7)):
				/* Change in signalling */
				break;
			case 0:
				flags &= ~XP_STATE_RSAZ;
				flags &= ~XP_STATE_RSAO;
				break;
			}
		}
		/* SR1.6: RDMA: Receive Distant MF Alarm.  Set when bit 6 of time slot 16 in frame 0 has been 
		   set for two consecutive multiframes.  This alarms is not disabled in the CCS signalling
		   mode. */
		if (status & (1 << 6)) {
			flags |= XP_STATE_RDMA;
		} else if (mask & (1 << 6)) {
			flags &= ~XP_STATE_RDMA;
		}
		/* SR1.4: RSLIP: Receive-Side Elastic Store Slip.  Set when the elastic store has either
		   repeated or deleted a frame of data. */
		if (status & (1 << 4)) {
			errors |= XP_ERR_CSS;
		}
		/* SR1.3: RUA1: Receive Unframed all Ones.  Set when an unframed all ones code is received at 
		   RPOSI and RNEGI. */
		if (status & (1 << 3)) {
			flags |= XP_STATE_RUA1;
		} else if (mask & (1 << 3)) {
			flags &= ~XP_STATE_RUA1;
		}
		/* SR1.2: RRA: Receive Remote Alarm.  Set when a remote alarm is received at RPOSI and RNEGI. 
		 */
		if (status & (1 << 2)) {
			flags |= XP_STATE_RRA;
		} else if (mask & (1 << 2)) {
			flags &= ~XP_STATE_RRA;
		}
		/* SR1.1: RCL: Receive Carrier Loss.  Set when 255 (or 2048 if CCR3.0 = 1) consecutive zeros
		   have been detected at RTIP and RRING.  (Note: a receiver carrier loss based on data
		   received at RPOSI and RNEGI is available in the HSR register). */
		if (status & (1 << 1)) {
			flags |= XP_STATE_LRCL;
		} else if (mask & (1 << 1)) {
			flags &= ~XP_STATE_LRCL;
		}
		/* SR1.0: RLOS: Receive Loss of Sync.  Set then the device is not synchronized to the receive 
		   E1 stream. */
		if (status & (1 << 0)) {
			flags |= XP_STATE_RLOS;
		} else if (mask & (1 << 0)) {
			flags &= ~XP_STATE_RLOS;
		}
	}

	if ((mask = xlb[0x17])) {	/* IMR2 */
		/* write-read-write cycle */
		xlb[0x07] = mask;	/* SR2 */
		status = xlb[0x07] & mask;
		xlb[0x07] = status;
		/* SR2.7: RMF: Receive CAS Multiframe.  Set every 2ms (regardelss if CAS signaling is enabled 
		   or not) on receive multiframe boundaries.  Used to alert the host that signalling data is
		   available. */
		if (status & (1 << 7)) {
		} else if (mask & (1 << 7)) {
		}
		/* SR2.6: RAF: Receive Align Frame.  Set every 250ns at the beginning of align frames.  Used
		   to alert the host that Si and Sa bits are available in the RAF and RNAF registers. */
		if (status & (1 << 6)) {
		} else if (mask & (1 << 6)) {
		}
		/* SR2.5: TMF: Transmit Multiframe.  Set every 2ms (regardless if CRC4 is enabled) on
		   transmit mutiframe boundaries.  Use to alert the host that signalling data needs to be
		   updated. */
		if (status & (1 << 5)) {
		} else if (mask & (1 << 5)) {
		}
		/* SR2.4: SEC: One Second timer.  Set on increments of one second based on RCLK. If CCR2.7 =
		   1, then this bit will be set every 62.5ms instead of once a second. */
		if (status & (1 << 4)) {
			timeout = 1;
		}
		/* SR2.3: TAF: Transmit Align Frame.  Set every 250ns at the beginning of align frames. Used
		   to alert the host that the TAF and TNAF registers need to be updated. */
		if (status & (1 << 3)) {
		} else if (mask & (1 << 3)) {
		}
		/* SR2.2: LOTC: Loss of transmit clock.  Set when TCLK pin has not transitioned for one
		   channel time (or 3ns).  Will force the LOTC pin high if enabled via TCR2.0. */
		if (status & (1 << 2)) {
		} else if (mask & (1 << 2)) {
		}
		/* SR2.1: RCMF: Receive CRC4 Multiframe.  Set on CRC4 multiframe boundaries; will continue to 
		   be set every 2ms on an arbitrary boundary if CRC4 is disabled. */
		if (status & (1 << 1)) {
		} else if (mask & (1 << 1)) {
		}
		/* SR2.0: TSLIP: Transmit Elastic Store Slip.  Set when the elastic store has either repeated 
		   or deleted a frame of data. */
		if (status & (1 << 0)) {
		} else if (mask & (1 << 0)) {
		}
	}

	/* Note: at this point, any Dallas interrupt should have been cleared by reading the interrupt bits.
	   Dallas interrupts generated by the IMR registers are acknowledged by reading the corresponding bit 
	   of the SR registers. */

	if (timeout)
		xp_e1_process_timeout(cd, sp, xlb, flags);

	/* Note that alarms are processed asyncrhonously whereas interface state is only processed during
	   timeouts. This keeps the interface state from thrashing around and detection mechanisms falsely
	   triggering on transient conditions. */
	if ((flags ^ sp->status.flags) != 0)
		xp_e1_process_alarms(cd, sp, xlb, flags, timeout);
}

/** xp_e1_process_timeout: - process timeout for a span
  * @cd: card structure (locked)
  * @sp: span structure
  * @xlb: span iobase pointer
  * @flags: span flags
  */
static noinline fastcall __hot void
xp_e1_process_timeout(struct cd *cd, struct sp *sp, volatile uint8_t *xlb, register uint flags)
{
	register uint mask, status;
	register uint errors = sp->status.errors;
	uint count;

	/* Read information registers and report */
	mask = 0xff;
	/* write-read-write cycle */
	xlb[0x08] = mask;	/* RIR */
	status = xlb[0x08] & mask;
	xlb[0x08] = status;
	if (status) {
		/* RIR.7: TESF: Transmit-side Elastic Store Full.  Set when the transmit-side eleastic store
		   buffer fills and a frame is deleted. */
		if (status & (1 << 7)) {
			errors |= XP_ERR_CSS;
		}
		/* RIR.6: TESE: Transmit-side Elastic Store Empty.  Set when the transmit-side elastic store
		   buffer empties and a frame is repeated. */
		if (status & (1 << 6)) {
			errors |= XP_ERR_CSS;
		}
		/* RIR.5: JALT: Jitter Attenuator Limit Trip.  Set when the jitter attenuator FIFO reaches to 
		   within 4-bits of its limit; useful for debugging jitter attenuation operation. */
		if (status & (1 << 5)) {
		}
		/* RIR.4: RESF: Receive-side Elastic Store Full.  Set when the receive side eleastic store
		   buffer fills and a frame is deleted. */
		if (status & (1 << 4)) {
			errors |= XP_ERR_CSS;
		}
		/* RIR.3: RESE: Receive-side Elastic Store Empty.  Set when the receive side elastic store
		   buffer empties and a frame is deleted. */
		if (status & (1 << 3)) {
			errors |= XP_ERR_CSS;
		}
		/* RIR.2: CRCRC: CRC Resync Criteria Met.  Set when 915/1000 codewords are received in error. 
		 */
		if (status & (1 << 2)) {
		}
		/* RIR.1: FASRC: FAS Resync Criteria Met.  Set when three consecutive FAS words are received
		   in error. Note: During a CRC resync the FAS synchronizer is brought online to verify the
		   FAS alignment.  If during this process a FAS emulator exists, the FAS synchronizer may
		   temporarily align to the emulator. The FASRC will go active indicating a search for a
		   valid FAS has been activated. */
		if (status & (1 << 1)) {
		}
		/* RIR.0: CASRC: CAS Resync Criteria Met.  Set when two consecutive CAS MF alignment words
		   are received in error. */
		if (status & (1 << 0)) {
		}
	}

	mask = 0xff;
	/* write-read-write cycle */
	xlb[0x1e] = mask;	/* SSR */
	status = xlb[0x1e] & mask;
	xlb[0x1e] = status;
	/* SSR.7-5: CRC4 Sync Counter Bit 5-2. */
	/* SSR.3: CRC4 Sync Counter Bit 0. */
	if (status & 0x07) {
		/* SSR.0: CRC4 MF Sync Active.  Set while the synchronizer is searching for the CRC MF
		   alignment word. */
		if (status & (1 << 0))
			flags &= ~XP_STATE_CRC;
		else
			flags |= XP_STATE_CRC;
		if (status & (1 << 0)) {
			/* The count of the number of failed 8-ms CRC-4 alignment attempts that have occurred 
			   since the last sucessful CRC-4 alignment.  This is for G.706 Annex B operation.
			   When the CRC-4 alignment has failed for 400-ms (after achieving basic frame
			   alignment), the CRC-4 capable equipment should assume that the other end is
			   incapable of generating CRC-4 multiframes.A In this case, the transmitter
			   continues transmitting CRC-4 multiframes; however, the E-bits should be set to
			   zero (indicating distant MF alarm).  The receiver; however, can be marked as
			   non-CRC-4. */
			count = ((status & 0xf0) >> 2) + ((status & 0x08) >> 2);
			if (count >= 49) {
				/* Basically we have 488 - 408 = 80 ms to determine that there has been a
				   CRC-4 alignment failure for more than 392 milliseconds. When ECUS is set
				   for 62.5 ms operation, we will detect within 31.25 ms of the event that
				   the CRC-4 has failed for 392 milliseconds or more (i.e. before the CRC-4
				   counter rolls over). */
				/* --1100-0 = 48 = 384 ms */
				/* --1100-0 = 50 = 400 ms */
				/* --1100-1 = 49 = 392 ms */
				/* --1100-1 = 51 = 408 ms */
				/* --1101-0 = 52 = 416 ms */
				/* --1101-0 = 54 = 432 ms */
				/* --1101-1 = 53 = 424 ms */
				/* --1101-1 = 55 = 440 ms */
				/* --1110-0 = 56 = 448 ms */
				/* --1110-0 = 58 = 464 ms */
				/* --1110-1 = 57 = 456 ms */
				/* --1110-1 = 59 = 472 ms */
				/* --1111-0 = 60 = 480 ms */
				/* --1111-0 = 62 = 496 ms */
				/* --1111-1 = 61 = 488 ms */
				/* --1111-1 = 63 = 504 ms */
				/* --0000-0 = 64 = 512 ms */
			}
		}
		/* SSR.1: CASSA: CAS MF Sync Active.  Set while the synchronizer is searching for the CAS MF
		   alignment word. */
		if (status & (1 << 1))
			flags &= ~XP_STATE_CAS;
		else
			flags |= XP_STATE_CAS;
		/* SSR.2: FASSA: FAS Sync Active.  Set while the syncrhonizer is searching for alignment at
		   the FAS level. */
		if (status & (1 << 2))
			flags &= ~XP_STATE_FAS;
		else if (mask & (1 << 2))
			flags |= XP_STATE_FAS;
	}

	/* BPV or Code Violation Counter */
	/* The 16-bit violation count records either Bipolar Violations (BPVs) or Code Violations (CVs).  If
	   CCR2.6 = 0, then the VCR counts bipolar violations. Bipolar violations are defined as consecutive
	   marks of the same polarity.  in this mode, if the HDB3 mode is set for the receive side via
	   CCR1.2, then HDB3 codewords are not counted as BPVs.  If CCR2.6 = 1, then the VCR counts code
	   violations as defined in ITU O.161.  Code violations are defined as consecutive bipolar violations 
	   of the same polarity.  In most applications, the framer should be set to count BPVs when receiving 
	   AMI code and to count CVs when receiving HDB3 code.  This counter increments at all times and is
	   not disabled by loss of sync conditions.  The counter saturates at 65,535 and will not rollover.
	   The bit error rate on an E1 line would have to be greater than 10E-02 before the VCR would
	   saturate. */
	/* NOTE: these are LCVs */
	if ((count = ((uint) (xlb[0x00] & 0xff) << 8) | ((uint) (xlb[0x01] & 0xff) >> 0))) {
		sp->stats[0].LCVs += count;
		errors |= XP_ERR_LCV;
	}
	/* CRC4 Error Counter */
	/* The 10-bit CRC4 error counter records word errors in the CRC4.  Since the maximum CRC4 count in a
	   one second period is 1000, this counter cannot saturate.  The counter is disabled during loss of
	   sync at either the FAS or CRC4 level; it will continue to count if loss of multiframe sync occurs
	   at the CAS level. */
	if ((count = ((uint) (xlb[0x02] & 0x03) << 8) | ((uint) (xlb[0x03] & 0xff) >> 0))) {
		/* NOTE: these are basically PCVs when in the CRC4 mode. */
		if (sp->config.ifgcrc == SDL_GCRC_CRC4) {
			sp->stats[0].PCVs += count;
			errors |= XP_ERR_PCV;
		}
	}
	/* E-Bit Counter */
	/* The 10-bit E-Bit counter records the Far-End Block Errors (FEBE) as reported in the first bit of
	   frames 13 and 15 on E1 lines running with CRC4 multiframe. These count registers will increment
	   once each time the received E-bit is set to zero.  Since the maximum E-bit count in a one second
	   period is 1000, this counter cannot saturate.  The counter is disabled during loss of sync at
	   either the FAS or CRC4 level; it will continue to count if loss of multiframe sync occurs at the
	   CAS level. */
	if ((count = ((uint) (xlb[0x04] & 0x03) << 8) | ((uint) (xlb[0x05] & 0xff) >> 0))) {
	}
	/* FAS Error Counter */
	/* The 12-bit FAS counter records the word errors in the Frame Alignment Signal in time slot 0.  This 
	   counter is disabled when RLOS is high.  FAS errors will not be counted when the framer is
	   searching for FAS alignment and/or synchronization at either the CAS or CRC4 multiframe level.
	   Since the maximum FAS word error count in a one second period is 4000, this counter cannot
	   saturate. */
	if ((count = ((uint) (xlb[0x02] & 0xfc) >> 4) | ((uint) (xlb[0x04] & 0xfc) >> 2))) {
		/* NOTE: these are basically PCVs when not in the CRC4 mode. */
		if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
			sp->stats[0].PCVs += count;
			errors |= XP_ERR_PCV;
		}
	}

	xp_e1_process_state(cd, sp, xlb, flags, errors);

	/* CCR2.7: ECUS: 0 = 8000 frames, 1 = 500 frames */
	if ((sp->interval += (xlb[0x1a] & 0x80) ? 500 : 8000) >= 8000) {
		sp->interval -= 8000;
		/* this is a 1 second timeout */
		xp_e1_process_stats(sp, errors);
		sp->status.errors = 0;
	} else
		sp->status.errors = errors;
	return;
}

/** xp_e1_process_state: - process state transitions for the device.
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span iobase
  * @flags: state flags (read only)
  * @errors: error flags (read only)
  *
  * Process state transitions once a timeout.  This is to ensure that the interface state does not
  * change due to transient conditions of less than 40 ms.  Also, any events or errors here have
  * accumulated for the entire timeout period (more than 40 ms).
  *
  * Processing DS21354/554 state transitions is more difficult because there is no transmitter
  * open-circuit detection capability on the chip, making transmitter state difficult to determine.
  * Also, receiver input level determination is rather limited in range.  Both these deficiencies
  * make it difficult to automatically determine whether a span is in a monitor configuration or not
  * and whether linear gain should be applied to the receivers.
  *
  * The approach is to take the transmitters offline altogether, and check whether a synced receive
  * side is indicating a yellow alarm.  When the receiver is synced and there is no yellow alarm,
  * the transmitters are not connected to the line.  When the receivers won't sync but there is no
  * carrier loss, we can use the input level somewhat, and possibly some errors, to determine
  * whether linear gain should be applied.  When there is no carrier for some time, we can go ahead
  * and try applying linear gain.  A new line interface receive carrier loss should start the
  * process all over again so that when a line is disconnected and then reconnected, it will
  * autoconfigure again.
  *
  * To perform all of these actions, we run a state machine and keep a state variable in
  * sp->status.state.  A state transition is performed at most once every 42/62 ms (short timeout).
  * The current state and the condition of the state flags and current errors are used to determine
  * the next state.  If we are not autoconfiguring anything, we do not run the state machine.
  */
static noinline fastcall __hot void
xp_e1_process_state(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, const register uint flags, const
		    register uint errors)
{
	register uint ifflags = sp->config.ifflags;

	/* integrate receiver state */
	if (flags & (XP_STATE_LRCL | XP_STATE_FRCL)) {
		ifflags &= ~SDL_IF_RX_UP;
		ifflags &= ~SDL_IF_RX_RUNNING;
	} else {
		ifflags |= SDL_IF_RX_UP;
		if (flags & (XP_STATE_RLOS | XP_STATE_RUA1 | XP_STATE_LORC))
			ifflags &= ~SDL_IF_RX_RUNNING;
		else
			ifflags |= SDL_IF_RX_RUNNING;
	}

	/* integrate transmitter state */
	if (flags & (XP_STATE_TOCD | XP_STATE_TSCD)) {
		ifflags &= ~SDL_IF_TX_UP;
		ifflags &= ~SDL_IF_TX_RUNNING;
	} else {
		ifflags |= SDL_IF_TX_UP;
		if (flags & (XP_STATE_LOLITC | XP_STATE_LOTC))
			ifflags &= ~SDL_IF_TX_RUNNING;
		else
			ifflags |= SDL_IF_TX_RUNNING;
	}

	/* integrate interface state */
	if (flags & (SDL_IF_RX_UP | SDL_IF_TX_UP))
		ifflags |= SDL_IF_UP;
	else
		ifflags &= ~SDL_IF_UP;

	/* detect changes in transmitter state */
	if ((ifflags ^ sp->config.ifflags) & (SDL_IF_TX_UP | SDL_IF_TX_RUNNING)) {
	}

	/* detect changes in receiver state */
	if ((ifflags ^ sp->config.ifflags) & (SDL_IF_RX_UP | SDL_IF_RX_RUNNING)) {
	}

	/* detect changes in interface state */
	if ((ifflags ^ sp->config.
	     ifflags) & (SDL_IF_UP | SDL_IF_TX_UP | SDL_IF_RX_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)) {
	}

	/* perform autoconfiguration when necessary */
	if (ifflags & SDL_IF_AUTOCONFIG) {
		if (likely(sp->status.state == 40)) {
			if (unlikely((flags & (XP_STATE_LRCL)) != 0))
				/* lost carrier, start full autoconfig */
				sp->status.state = 0;
			else if (unlikely((flags & (XP_STATE_FRCL)) != 0))
				/* lost signal, start partial autoconfig */
				sp->status.state = 20;
		}
		if (unlikely(sp->status.state != 40))
			/* lost configuration hold, process autoconfig */
			xp_e1_process_auto(cd, sp, xlb, flags, errors, 1);
	}
}

/** xp_e1_process_auto: - process autoconfiguration state machine
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span iobase
  * @flags: state flags
  * @errors: error events
  * @timeout: true when timeout
  *
  * Note that we do not even execute this function unless SDL_IF_AUTOCONFIG is set in the interface
  * flags.  The function is executed on regular interrupts as well as timeout interrupts.  The
  * @timeout argument is set when the interrupt was as a result of a timeout, and Unset otherwise.
  * This permits having the autodetection state machine run very quickly for expected
  * configurations.
  *
  * The DS21354/554 chip can perform linear gain for monitoring applications of 12dB or 30dB.  30dB
  * corresponds to 1800 Ohm isolation resistors (or a triple 470 Ohm high-impedance monitoring tap).
  * 12dB linear gain (corresponding to 180 Ohm isolation resistors) is not useful for monitoring
  * applications.
  *
  */
static noinline fastcall __unlikely void
xp_e1_process_auto(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint flags, const
		   register uint errors, const uint timeout)
{
	register unsigned int state = sp->status.state;
	const uint interval = timeout ? ((xlb[0x1a] & 0x80) ? 500 : 8000) : 0;

	for (;;) {
		do {
			if (flags & XP_STATE_LIRST) {
				/* resetting line interface */
				sp->status.count = 0;
				sp->status.config = 0;
				state = 0;
				break;
			}
			if ((flags & XP_STATE_LRCL) && (8 <= state)) {
				/* lost carrier */
				sp->status.count = 0;
				state = 0;
				break;
			}
			if ((flags & XP_STATE_FRCL) && (9 <= state)) {
				/* lost signal */
				sp->status.count = 0;
				state = 8;
				break;
			}
			if ((flags & XP_STATE_RUA1) && (10 <= state)) {
				/* lost service */
				sp->status.count = 0;
				state = 9;
				break;
			}
			if ((flags & XP_STATE_RLOS) && (11 <= state && state < 13)) {
				/* lost sync */
				sp->status.count = 0;
				state = 10;
				break;
			}
		} while (0);
		switch (state) {
		default:
			state = 0;
			/* fall through */
		case 0:
			/* LIRST subsidance.  Wait 125 ms or more when the LIRST condition remains, then
			   clear the conditioni and move on to line detection. */
			if (flags & (XP_STATE_LIRST)) {
				if ((sp->status.count += interval) < 1000) {
					/* wait for up to 125 ms */
					break;
				}
				/* remove LIRST flag */
				sp->status.flags &= ~(XP_STATE_LIRST);
				flags &= ~(XP_STATE_LIRST);
				/* reset elastic stores */
				/* CCR6.1: 0->1 RES reset */
				/* CCR6.0: 0->1 TES reset */
				xlb[0x1d] &= ~((1 << 1) | (1 << 0));
				xlb[0x1d] |= (1 << 1) | (1 << 0);
				xlb[0x1d] &= ~((1 << 1) | (1 << 0));
				/* realign elastic stores */
				/* CCR5.6: 0->1 RES align (not assigned DS2154) */
				/* CCR5.5: 0->1 TES align (not assigned DS2154) */
				xlb[0xaa] &= ~((1 << 6) | (1 << 5));
				xlb[0xaa] |= (1 << 6) | (1 << 5);
				xlb[0xaa] &= ~((1 << 6) | (1 << 5));
			}
			/* remove linear gain */
			/* TEST3: 0x00 = no Rx gain */
			xlb[0xac] = 0x00;
			/* start transmitter, if not already started */
			/* LICR.0: transmitters 0 = on; 1 = off */
			xlb[0x18] &= ~(1 << 0);
			/* send unframed all ones if not sending them already */
			/* TCR1.4: 0 = normal data, 1 = unframed all ones */
			xlb[0x12] |= (1 << 4);
			sp->status.count = 0;
			sp->config.ifflags &= ~(SDL_IF_UP | SDL_IF_RX_UP | SDL_IF_TX_UP | SDL_IF_RX_MON);
			state = 1;
			/* fall through */
		case 1:
			/* Line carrier detect, 0dB gain. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying linear gain */
					/* apply 12dB linear gain */
					/* TEST3: 0x72 = 12dB Rx gain */
					xlb[0xac] = 0x72;
					sp->status.count = 0;
					state = 2;
					continue;
				}
				/* wait for up to 125 ms */
				break;
			}
			/* start transmitter, if not already started */
			/* LICR.0: transmitters 0 = on; 1 = off */
			xlb[0x18] &= ~(1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 2:
			/* Line carrier detect, monitoring, 12dB gain.  No line carrier was detected with 0dB 
			   linear gain, try applying 12dB gain.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying more linear gain */
					/* apply 30dB linear gain */
					/* TEST3: 0x70 = 30dB Rx gain */
					xlb[0xac] = 0x70;
					sp->status.count = 0;
					state = 3;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 5;
			continue;
		case 3:
			/* Line carrier detect, monitoring, 30dB gain.  No line carrier was detected wtih 0dB 
			   and 12dB linear gain, try applying 30dB gain.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try removing gain again */
					/* apply 0dB linear gain */
					/* TEST3: 0x00 = no Rx gain */
					xlb[0xac] = 0x00;
					sp->status.count = 0;
					state = 1;
					continue;
				}
				/* wait up to 125 ms */
				break;
			}
			/* apply 12dB linear gain */
			sp->status.count = 0;
			state = 4;
			/* fall through */
		case 4:
			/* Line carrier detected, monitoring, 12dB gain.  30dB gain was applied previously
			   with success, drop back to 12dB gain to test that things were not just connected
			   at a bad time. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 30dB gain */
					/* apply 30dB linear gain */
					/* TEST3: 0x70 = 30dB Rx gain */
					xlb[0xac] = 0x70;
					sp->status.count = 0;
					state = 6;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* apply 0dB linear gain */
			sp->status.count = 0;
			state = 5;
			/* fall through */
		case 5:
			/* Line carrier detected, monitoring, 0dB gain.  12dB gain was applied perviously
			   wtih success, drop back to 0dB gain to test that things were not just connected at 
			   a bad time. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 12dB gain */
					/* apply 12dB linear gain */
					/* TEST3: 0x72 = 12dB Rx gain */
					xlb[0xac] = 0x72;
					sp->status.count = 0;
					state = 7;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* start transmitter, if not already started */
			/* LICR.0: transmitters 0 = on; 1 = off */
			xlb[0x18] &= ~(1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 6:
			/* Line carrier detected, monitoring, 30dB gain.  30dB worked, 12dB didn't, go back
			   to 30dB an lock in if successful. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 30dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					/* TEST3: 0x00 = no Rx gain */
					xlb[0xac] = 0x00;
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* stop transmitter, if not already stopped */
			/* LICR.0: transmitters 0 = on; 1 = off */
			xlb[0x18] |= (1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 7:
			/* Line carrier detected, monitoring, 12dB gain.  12dB worked, 0dB didn't, go back to 
			   12dB an lock in if successful. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 12dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					/* TEST3: 0x00 = no Rx gain */
					xlb[0xac] = 0x00;
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* stop transmitter, if not already stopped */
			/* LICR.0: transmitters 0 = on; 1 = off */
			xlb[0x18] |= (1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 8;
			continue;
		case 8:
			/* Framer signal detect.  Line carrier detected, 0dB, 12dB or 20dB linear gain
			   applied.  When other than 0dB linear gain is applied, SDL_IF_RX_MON flag is set. */
			if (flags & (XP_STATE_FRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* no framer signal, start again */
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 9;
			/* fall through */
		case 9:
			/* Framing detect, sending unframed all ones.  If we are in a loop-around mode we
			   will wait here for manual (specific) configuration because we will be both sending 
			   and receiving unframed all ones.  However, only wait for 10 seconds and then
			   restart in case there is an error in the state machine that locks us here. */
			if (flags & (XP_STATE_RUA1)) {
				if ((sp->status.count += interval) >= 10 * 8000) {
					/* no service for 10 seconds, start again */
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait up to 10 seconds */
				break;
			}
			sp->status.count = 0;
			state = 10;
			/* fall through */
		case 10:
			/* Frame detect, not receiving unframed all ones. */
			if (flags & (XP_STATE_RLOS)) {
				if ((sp->status.count += interval) >= 3200) {
					/* no sync after 400 ms */
					/* reconfigure and try again */
					if (xp_e1_change_config(sp, xlb)) {
						/* exhausted possiblilities, start again */
						sp->status.count = 0;
						state = 0;
						continue;
					}
					sp->status.count = 0;
					/* wait to test possibility */
					break;
				}
				/* wait for up to 400 ms */
				break;
			}
			sp->status.count = 0;
			state = 11;
			/* fall through */
		case 11:
			/* Transmitter detection, transmitting unframed all ones.  We are transmitting
			   unframed all ones, so when there is no remote alarm after 125 ms the transmitters
			   must be disconnected. */
			if (!(flags & (XP_STATE_RYEL | XP_STATE_RRA))) {
				if ((sp->status.count += interval) >= 1000) {
					/* No alarm after 125 milliseconds, transmitters must be
					   disconnected.  When no linear gain is applied, but transmitters
					   are disconnected: this is a 0dB active monitoring tap.  When
					   linear gain applied, already monitoring so this is an expected
					   result.  */
					/* stop transmitter, if not already stopped */
					/* LICR.0: transmitters 0 = on; 1 = off */
					xlb[0x18] |= (1 << 0);
					sp->config.ifflags |= SDL_IF_RX_MON;
					sp->config.ifflags &= ~(SDL_IF_TX_UP);
					sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
					sp->status.count = 0;
					state = 13;
					continue;
				}
				/* wait up to 125 ms */
				break;
			}
			/* transmit normal frames */
			/* TCR1.4: 0 = normal data, 1 = unframed all ones */
			xlb[0x12] &= ~(1 << 4);
			sp->status.count = 0;
			state = 12;
			/* fall through */
		case 12:
			/* Transmitter detection, sending normal frames.  We previously had a remote alarm
			   condition when sending unframed all ones.  We start sending normal frames and
			   check that the alarm clears within 10 seconds.  */
			if (flags & (XP_STATE_RYEL | XP_STATE_RRA)) {
				if ((sp->status.count += interval) < 10 * 8000) {
					/* wait up to 10 seconds */
					break;
				}
				/* Alarms did not clear after 10 seconds, but this might just be a sync
				   problem. */
			}
			/* start transmitter, if not already started */
			/* LICR.0: transmitters 0 = on; 1 = off */
			xlb[0x18] &= ~(1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->config.ifflags |= (SDL_IF_TX_UP);
			sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
			sp->status.count = 0;
			state = 13;
			/* fall through */
		case 13:
			/* Stable state, normal mode, transmitters externally connected, sending normal
			   frames.  This is an active connection, not a monitoring tap. Flags have been set
			   accordingly.  Stay in this state unless we lose carrier, signal, start receiving
			   unframed all ones, or lose sync for more than 400 ms. */
			/* Stable state, monitoring mode, transmitters externally disconnected, sending
			   unframed all ones.  This is a monitoring tap (active or passive).  Flags have been 
			   set accordingly.  Stay in this state unless we lose carrier, signal, start
			   receiving unframed all ones, or lose sync for more than 400 ms. */
			if (flags & (XP_STATE_RLOS)) {
				/* lost sync */
				if ((sp->status.count += interval) >= 3200) {
					/* send unframed all ones */
					/* TCR1.4: 0 = normal data, 1 = unframed all ones */
					xlb[0x12] |= (1 << 4);
					sp->status.count = 0;
					state = 10;
					continue;
				}
				/* wait for up to 400 ms */
				break;
			}
			sp->status.count = 0;
			/* stay in this state */
			break;
		}
		break;
	}
	/* set the new state variable */
	sp->status.state = state;
}

/** xp_e1_change_config: - try changing span configuration for sync
  * @sp: span structure pointer
  * @xlb: span iobase
  *
  * Diagnose the problem and attempt to change the configuration accordingly. This is an E1 line so
  * likely difficulties are CCS instead of CAS; CRC4 instead of non-CRC4; HDB3 instead of AMI.  We
  * should simply walk through the possible configurations until one that works is found or all are
  * exhausted.
  */
noinline __unlikely int
xp_e1_change_config(struct sp *sp, register volatile uint8_t *xlb)
{
	switch (sp->status.config) {
	case 0:		/* CCS, HDB3, CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CCS) {
			sp->config.ifframing = SDL_FRAMING_CCS;
			xlb[0x14] |= (1 << 3);	/* CCR1.3: 1 = Rx CCS */
			xlb[0x12] &= ~(1 << 5);	/* TCR1.5: 0 = ch 16 data */
		}
		if (sp->config.ifcoding != SDL_CODING_HDB3) {
			sp->config.ifcoding = SDL_CODING_HDB3;
			xlb[0x14] |= (1 << 6);	/* CCR1.6: 1 = Tx HDB3 */
			xlb[0x14] |= (1 << 2);	/* CCR1.2: 1 = Rx HDB3 */
			xlb[0x1a] |= (1 << 6);	/* CCR2.6: 1 = count CVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] &= ~(1 << 0);	/* CCR1.0: 0 = Rx no CRC4 */
		}
		sp->status.config = 1;
		break;
	case 1:		/* CCS, HDB3, non-CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CCS) {
			sp->config.ifframing = SDL_FRAMING_CCS;
			xlb[0x14] |= (1 << 3);	/* CCR1.3: 1 = Rx CCS */
			xlb[0x12] &= ~(1 << 5);	/* TCR1.5: 0 = ch 16 data */
		}
		if (sp->config.ifcoding != SDL_CODING_AMI) {
			sp->config.ifcoding = SDL_CODING_AMI;
			xlb[0x14] &= ~(1 << 6);	/* CCR1.6: 0 = Tx AMI */
			xlb[0x14] &= ~(1 << 2);	/* CCR1.2: 0 = Rx AMI */
			xlb[0x1a] &= ~(1 << 6);	/* CCR2.6: 0 = count BPVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] |= (1 << 0);	/* CCR1.0: 1 = Rx CRC4 */
		}
		sp->status.config = 2;
		break;
	case 2:		/* CCS, AMI, CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CCS) {
			sp->config.ifframing = SDL_FRAMING_CCS;
			xlb[0x14] |= (1 << 3);	/* CCR1.3: 1 = Rx CCS */
			xlb[0x12] &= ~(1 << 5);	/* TCR1.5: 0 = ch 16 data */
		}
		if (sp->config.ifcoding != SDL_CODING_AMI) {
			sp->config.ifcoding = SDL_CODING_AMI;
			xlb[0x14] &= ~(1 << 6);	/* CCR1.6: 0 = Tx AMI */
			xlb[0x14] &= ~(1 << 2);	/* CCR1.2: 0 = Rx AMI */
			xlb[0x1a] &= ~(1 << 6);	/* CCR2.6: 0 = count BPVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
			sp->config.ifgcrc = SDL_GCRC_CRC5;
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] &= ~(1 << 0);	/* CCR1.0: 0 = Rx no CRC4 */
		}
		sp->status.config = 3;
		break;
	case 3:		/* CCS, AMI, non-CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CAS) {
			sp->config.ifframing = SDL_FRAMING_CAS;
			xlb[0x14] &= ~(1 << 3);	/* CCR1.3: 0 = Rx CAS */
			xlb[0x12] |= (1 << 5);	/* TCR1.5: 1 = ch 16 sig */
		}
		if (sp->config.ifcoding != SDL_CODING_HDB3) {
			sp->config.ifcoding = SDL_CODING_HDB3;
			xlb[0x14] |= (1 << 6);	/* CCR1.6: 1 = Tx HDB3 */
			xlb[0x14] |= (1 << 2);	/* CCR1.2: 1 = Rx HDB3 */
			xlb[0x1a] |= (1 << 6);	/* CCR2.6: 1 = count CVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
			sp->config.ifgcrc = SDL_GCRC_CRC4;
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] |= (1 << 0);	/* CCR1.0: 1 = Rx CRC4 */
		}
		sp->status.config = 4;
		break;
	case 4:		/* CAS, HDB3, CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CAS) {
			sp->config.ifframing = SDL_FRAMING_CAS;
			xlb[0x14] &= ~(1 << 3);	/* CCR1.3: 0 = Rx CAS */
			xlb[0x12] |= (1 << 5);	/* TCR1.5: 1 = ch 16 sig */
		}
		if (sp->config.ifcoding != SDL_CODING_HDB3) {
			sp->config.ifcoding = SDL_CODING_HDB3;
			xlb[0x14] |= (1 << 6);	/* CCR1.6: 1 = Tx HDB3 */
			xlb[0x14] |= (1 << 2);	/* CCR1.2: 1 = Rx HDB3 */
			xlb[0x1a] |= (1 << 6);	/* CCR2.6: 1 = count CVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
			sp->config.ifgcrc = SDL_GCRC_CRC5;
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] &= ~(1 << 0);	/* CCR1.0: 0 = Rx no CRC4 */
		}
		sp->status.config = 5;
		break;
	case 5:		/* CAS, HDB3, non-CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CAS) {
			sp->config.ifframing = SDL_FRAMING_CAS;
			xlb[0x14] &= ~(1 << 3);	/* CCR1.3: 0 = Rx CAS */
			xlb[0x12] |= (1 << 5);	/* TCR1.5: 1 = ch 16 sig */
		}
		if (sp->config.ifcoding != SDL_CODING_AMI) {
			sp->config.ifcoding = SDL_CODING_AMI;
			xlb[0x14] &= ~(1 << 6);	/* CCR1.6: 0 = Tx AMI */
			xlb[0x14] &= ~(1 << 2);	/* CCR1.2: 0 = Rx AMI */
			xlb[0x1a] &= ~(1 << 6);	/* CCR2.6: 0 = count BPVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
			sp->config.ifgcrc = SDL_GCRC_CRC4;
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] |= (1 << 0);	/* CCR1.0: 1 = Rx CRC4 */
		}
		sp->status.config = 6;
		break;
	case 6:		/* CAS, AMI, CRC4 */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CAS) {
			sp->config.ifframing = SDL_FRAMING_CAS;
			xlb[0x14] &= ~(1 << 3);	/* CCR1.3: 0 = Rx CAS */
			xlb[0x12] |= (1 << 5);	/* TCR1.5: 1 = ch 16 sig */
		}
		if (sp->config.ifcoding != SDL_CODING_AMI) {
			sp->config.ifcoding = SDL_CODING_AMI;
			xlb[0x14] &= ~(1 << 6);	/* CCR1.6: 0 = Tx AMI */
			xlb[0x14] &= ~(1 << 2);	/* CCR1.2: 0 = Rx AMI */
			xlb[0x1a] &= ~(1 << 6);	/* CCR2.6: 0 = count BPVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
			sp->config.ifgcrc = SDL_GCRC_CRC5;
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] &= ~(1 << 0);	/* CCR1.0: 0 = Rx no CRC4 */
		}
		sp->status.config = 7;
		break;
	case 7:		/* CAS, AMI, non-CRC4 */
		/* last configuration failed, go back to default */
		sp->config.ifgtype = SDL_GTYPE_E1;
		if (sp->config.ifframing != SDL_FRAMING_CCS) {
			sp->config.ifframing = SDL_FRAMING_CCS;
			xlb[0x14] |= (1 << 3);	/* CCR1.3: 1 = Rx CCS */
			xlb[0x12] &= ~(1 << 5);	/* TCR1.5: 0 = ch 16 data */
		}
		if (sp->config.ifcoding != SDL_CODING_HDB3) {
			sp->config.ifcoding = SDL_CODING_HDB3;
			xlb[0x14] |= (1 << 6);	/* CCR1.6: 1 = Tx HDB3 */
			xlb[0x14] |= (1 << 2);	/* CCR1.2: 1 = Rx HDB3 */
			xlb[0x1a] |= (1 << 6);	/* CCR2.6: 1 = count CVs */
		}
		if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
			sp->config.ifgcrc = SDL_GCRC_CRC4;
			xlb[0x14] |= (1 << 4);	/* CCR1.4: 1 = Tx CRC4 */
			xlb[0x14] |= (1 << 0);	/* CCR1.0: 1 = Rx CRC4 */
		}
		sp->status.config = 0;
		return (1);
	}
	return (0);
}

/** xp_e1_process_stats: - process statistics (performance measures) for the span
  * @sp: span structure pointer
  * @errors: error events accumulated so far.
  *
  * ERROR EVENTS:
  * Bipolar Violation BPV Error Event: A BPV error event for an AMI coded signal is the occurrence
  *	of a pulse of the same polarity as the previous pulse.  A BPV error event for a B8ZS or HDB3
  *	coded signal is the occurrence of a pulse of the same polarity as the previous pulse without
  *	being part of the zero substitution code. 
  *
  * Controlled Slip (CS) Error Event: A CS is the replication or deletion of the payload bits of a
  *	DS1 frame.  A CS may be performed when there is a difference between the timing of a
  *	synchronous receiving terminal an the received signal.  A CS does not acuase an Out of Frame
  *	defect. 
  *
  * Excessive Zeros (EXZ) Error Event: An EXZ error event for an AMI coded signal is the occurence
  *	of more than fifteen contiguous zeros.  For a B8ZS coded signal, the defect occurs when more
  *	than seven contiguous zeros are detected. 
  *
  * Line Coding Violation (LCV) Error Event: An LCV is the occurence of either a Bipolar Violation
  *	or Excessive Zeros (EXZ) error event. 
  *
  * Path Coding Violation (PCV) Error Event: A PCV error event is a frame synchronization bit error
  *	in the D4 and E1-non-CRC-4 formats, or a CRC error in the ESF and E1-CRC-4 formats. 
  *
  * PERFORMANCE PARAMETERS:
  * All performance parameters are accumulated in fifteen minute intervals and up to 96 intervals
  * (covering a 24-hour period) are kept by an agent.  Fewer than 96 intervals of data will be
  * available if the agent has been restarted within the last 24 hours.  In addition, there is a
  * rolling 24-hour total of each performance parameter.  There is no requirement for an agent to
  * ensure a fixed relationship between the start of a fifteen minute interval and clock time;
  * however, some agents may align the fifteen minute intervals with quarter hours. 
  *
  * Severely Errored Seconds (SES): An SES for ESF signals is a second with one of the following:
  *	320 or more PCV error events; one or more OOF defects; a detected AIS defect. For E1 CRC-4
  *	signals, an SES is a second with either 832 or more PCV error events or one or more OOF
  *	defects.  For E1 non-CRC-4 signals, an SES is a 2048 (LCV ????) PCV or more. For D4 signals,
  *	an SES is a count of one-second intervals with Framing Error events, or an OOF defect, or
  *	1544 LCV or more.  Controlled slips are not included in this parameter. This is not
  *	incremented during an Unavailable Second.
  *
  * Severely Errored Framing Seconds (SEFS): An SEFS is a second with either one or more OOF
  *	defects, or a detected AIS defect.
  *
  * Unavailable Seconds (UASs):  A performance management event that is calculted by counting the
  *	number of seconds for which the interface is unavailable.  An interface is said to be
  *	unavailable from the onset of 10 continugous Severely Errored Seconds (SESs), or on the
  *	onset of a condition leading to a failutre. Once unavailable, and if no failure is present,
  *	an interface becomes available at the onset of 10 contiguous seconds with no SESs. 
  *
  *	UAS are calculated by counting the number of seconds that the interface is unavailable.  The
  *	DS1 interface is said to be unavailable from the onset of ten contiguous SESs, or the onset
  *	of the condition leadeing to a failure (see Failure States).  If the condition leading to
  *	the failure was immediately preceded by one or more continguous SES, then the DS1 interface
  *	unavailability starts from the onset of these SES. Once unavailable, and if no failure is
  *	present, the DS1 interfae becomes available at the onset of 10 contiguous seconds with no
  *	SES.  if the failure clearing time is less than or equat to ten seconds.  If the failure
  *	clearing time is more than ten seconds, the DS1 interface becoms available at the onset of
  *	tem contiguous seconds with no SES, or the onset period leading to the successful clearing
  *	condition, whichever occurs later.  With respect to DS1 error counts, all coutners are
  *	incremented while the DS1 interface is deemed available.  While the interface is deemed
  *	unavailable, the only count that is incremented is UAS.  A special case exists when the ten
  *	or more second period crosses the 900 second statistic window boundary, as the foregoing
  *	description ipmlies that the SES and UAS counter must be adjusted when the UAS stats is
  *	entered.  Successive "gets" of the affectetd dsx1IntervalSESs and dsx1IntervalUASs objects
  *	will return differing values if the first get occurs during the first few seconds of the
  *	window.  This is viewed as an unavoidable side-effect of selecting the presently-defined
  *	objects.  
  *
  * Bursty Errored Secods (BES): A BES (also known as an Errored Second Type B) is a second with
  *	fewer than 320 and more than one PCV, no SEF defects, and no detected incoming AIS defects.
*	Controlled slips are not incldued in this parameter. 
  *
  * Errored Seconds (ES): For ESF and E1-CRC links an Errored Second is a second with one of the
  *	followng: one or more PCV; one or more OOF defects; one or more Controlled Slip events; a
  *	detected AIS defect.
  *
  * Controlled Slip Seconds (CSS): A cCSS is a one-second interval containing one or more controlled
  *	slips.
  *
  * Line Errored Seconds (LES): A LES, according to T1M1.3, is a second in which one or more LCV
  *	error events were detected.  While many implementations are currently unable to detect the
  *	zero strings, it is expected that interface manufacturers will add this capability in
  *	deference to ANSI; therefore, it will become available in time. In the T1M1.3 specification,
  *	near end LCV and far end LES are counted.  For consistency, we count LES at both ends.
  *
  * Degraded Minutes: A DM is one in which the estimated error rate exceeds 1E-06 but does not
  *	exceed 1E-03.  DM are determined by collecting all of the Available Seconds, removing any
  *	Severely Errored Seconds grouping the result in 60-second long groups and counting a
  *	60-second long group (minute) as degraded if the cumulative errors during the seconds
  *	present in the group exceed 1E-6.  Available seconds are merely those seconds which are not
  *	unavailable as described below. 
  */
static noinline fastcall __hot void
xp_e1_process_stats(struct sp *sp, register uint errors)
{
	/* This is a one second timeout. */

	/* This is a one second timeout */

	/* Calculate severely errored seconds. */
	switch (sp->config.ifgcrc) {
	case SDL_GCRC_CRC4:
		/* For E1 CRC4 signals, an SES is 832 or more PCV error events, or one or more Out of Frame
		   defects. */
		if (sp->stats[0].PCVs >= 832)
			errors |= XP_ERR_SES;
		break;
	case SDL_GCRC_CRC5:
		/* For E1 non-CRC4 signals, an SES is 2048 or more LCV. */
		if (sp->stats[0].LCVs >= 2048)
			errors |= XP_ERR_SES;
		break;
	}

	/* The number of Unavailable Seconds (UASs) in the current interval. */
	/* Track the number of consecutive severely errored seconds and the number of consecutive
	   non-severely errored seconds */
	if (errors & (XP_ERR_SES)) {
		sp->status.nses = 0;
		sp->status.sess++;
		if (sp->status.sess >= 10) {
			if (sp->status.sess == 10 && !(errors & XP_ERR_UAS)) {
				sp->stats[0].UASs += 9;
				errors |= XP_ERR_UAS;
			}
			errors |= XP_ERR_UAS;
		}
	} else {
		sp->status.sess = 0;
		sp->status.nses++;
		if (sp->status.nses >= 10) {
			if (sp->status.nses == 10 && (errors & XP_ERR_UAS)) {
				sp->stats[0].UASs -= 9;
				errors &= ~XP_ERR_UAS;
			}
			errors &= ~XP_ERR_UAS;
		}
	}
	if (errors & (XP_ERR_UAS))
		sp->stats[0].UASs += 1;

	/* Calculate bursty errored seconds */
	switch (sp->config.ifgcrc) {
	case SDL_GCRC_CRC4:
		if (1 < sp->stats[0].PCVs && sp->stats[0].PCVs < 832)
			if (!(errors & XP_ERR_SEFS))
				if (!(errors & XP_FAIL_AIS))
					errors |= XP_ERR_BES;
		break;
	case SDL_GCRC_CRC5:
		if (1 < sp->stats[0].LCVs && sp->stats[0].LCVs < 2048)
			if (!(errors & XP_ERR_SEFS))
				if (!(errors & XP_FAIL_AIS))
					errors |= XP_ERR_BES;
		break;
	}

	/* The number of Errored Seconds (ESs) in the current interval. */
	if (sp->stats[0].PCVs > 0)
		errors |= XP_ERR_ES;
	if (errors & (XP_ERR_ES))
		sp->stats[0].ESs = 1;
	/* The number of severely errored seconds (SESs) in the current interval. */
	if (errors & (XP_ERR_SES))
		sp->stats[0].SESs = 1;
	/* The number of Severely Errored Framing Seconds (SEFSs) in the current interval. */
	if (errors & (XP_ERR_SEFS))
		sp->stats[0].SEFSs = 1;
	/* The number of controlled slip seconds (CSSs) in the current interval. */
	if (errors & (XP_ERR_CSS))
		sp->stats[0].CSSs = 1;
	/* The number of Line Errored Seconds (LESs) in the current interval. */
	if (errors & (XP_ERR_LES))
		sp->stats[0].LESs = 1;
	/* The number of Bursty Errored Seconds (BESs) in the current interval. */
	if (errors & (XP_ERR_BES))
		sp->stats[0].BESs = 1;

	/* accumulate the one-second counts */
	sp->stats[1].ESs += sp->stats[0].ESs;
	sp->stats[1].SESs += sp->stats[0].SESs;
	sp->stats[1].ESs += sp->stats[0].ESs;
	sp->stats[1].SESs += sp->stats[0].SESs;
	sp->stats[1].SEFSs += sp->stats[0].SEFSs;
	sp->stats[1].UASs += sp->stats[0].UASs;
	sp->stats[1].CSSs += sp->stats[0].CSSs;
	sp->stats[1].PCVs += sp->stats[0].PCVs;
	sp->stats[1].LESs += sp->stats[0].LESs;
	sp->stats[1].BESs += sp->stats[0].BESs;
	sp->stats[1].DMs += sp->stats[0].DMs;
	sp->stats[1].LCVs += sp->stats[0].LCVs;
	sp->stats[1].FASEs += sp->stats[0].FASEs;
	sp->stats[1].FABEs += sp->stats[0].FABEs;
	sp->stats[1].FEBEs += sp->stats[0].FEBEs;
	bzero(&sp->stats[0], sizeof(sp->stats[0]));

	if ((++sp->stats[1].SECs) == 60) {
		/* this is a minute timeout */
		/* The number of degraded minutes (DMs) in the current interval. */
		if (errors & (XP_ERR_DM))
			sp->stats[1].DMs = 1;

		/* accumulate one-minute counts */
		sp->stats[2].ESs += sp->stats[1].ESs;
		sp->stats[2].SESs += sp->stats[1].SESs;
		sp->stats[2].ESs += sp->stats[1].ESs;
		sp->stats[2].SESs += sp->stats[1].SESs;
		sp->stats[2].SEFSs += sp->stats[1].SEFSs;
		sp->stats[2].UASs += sp->stats[1].UASs;
		sp->stats[2].CSSs += sp->stats[1].CSSs;
		sp->stats[2].PCVs += sp->stats[1].PCVs;
		sp->stats[2].LESs += sp->stats[1].LESs;
		sp->stats[2].BESs += sp->stats[1].BESs;
		sp->stats[2].DMs += sp->stats[1].DMs;
		sp->stats[2].LCVs += sp->stats[1].LCVs;
		sp->stats[2].FASEs += sp->stats[1].FASEs;
		sp->stats[2].FABEs += sp->stats[1].FABEs;
		sp->stats[2].FEBEs += sp->stats[1].FEBEs;
		bzero(&sp->stats[1], sizeof(sp->stats[1]));

		if ((sp->stats[2].SECs += 60) == 900) {
			uint index = sp->curr;

			sp->hist[index] = sp->stats[2];
			sp->hist[index].ValidData = 2;	/* true(2) */
			bzero(&sp->stats[2], sizeof(sp->stats[2]));
			if (++index == 96)
				index = 0;
			sp->curr = index;
		}
	}
	return;
}

static noinline fastcall __hot void
xp_e1_process_alarms(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint flags,
		     uint timeout)
{
	register uint alarms = sp->config.ifalarms;

	/* integrate alarms */
	if ((flags & XP_STATE_RDMA) && (sp->config.ifframing == SDL_FRAMING_CAS))
		alarms |= SDL_ALARM_DMF;
	else
		alarms &= ~SDL_ALARM_DMF;
	if (flags & (XP_STATE_RRA | XP_STATE_RYEL))
		alarms |= SDL_ALARM_YEL;
	else
		alarms &= ~SDL_ALARM_YEL;
	if (flags & XP_STATE_RUA1)
		alarms |= SDL_ALARM_BLU;
	else
		alarms &= ~SDL_ALARM_BLU;
	if (flags & (XP_STATE_RLOS | XP_STATE_FRCL | XP_STATE_LRCL))
		alarms |= SDL_ALARM_RED;
	else
		alarms &= ~SDL_ALARM_RED;
	if ((alarms & SDL_ALARM_REC) && (timeout)) {
		if (alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
			alarms &= ~SDL_ALARM_REC;
		else {
			/* recovery time in terms of timeouts (5 seconds) */
			if (sp->recovertime && !--sp->recovertime) {
				/* Note that this should not have to be performed because we have set
				   Automatic RAI on, but it cannot hurt. */
				xlb[0x21] &= ~(1 << 5);	/* TNAF.5: A: 0 = no RAI */
				alarms &= ~SDL_ALARM_REC;
				cd->eval_syncsrc = 1;
			}
		}
	}
	/* if any of the alarms (or recovery) changed we need to do this */
	if ((alarms ^ sp->config.ifalarms)
	    & (SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC)) {
		if ((alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
		    && !(sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) {
			/* Local alarms have just begun.  Signal a yellow (RAI) alarm to the other end. */
			if (!(alarms & SDL_ALARM_REC)) {
				/* Note that this should not have to be performed because we have set
				   Automatic RAI on, but it cannot hurt. */
				xlb[0x21] |= (1 << 5);	/* TNAF.5: A: 1 = RAI */
				/* local alarms just begun and we were not in recovery */
				if (alarms & SDL_ALARM_RED)
					cd->eval_syncsrc = 1;
			} else
				alarms &= ~SDL_ALARM_REC;
		} else if ((alarms & SDL_ALARM_RED)
			   && !(sp->config.ifalarms & SDL_ALARM_RED)) {
			/* red alarm just got added to the set, reevaluate the sync source */
			cd->eval_syncsrc = 1;
		} else if (!(alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
			   && (sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) {
			/* Local alarms have just ended. */
			alarms |= SDL_ALARM_REC;
			if (xlb[0x1a] & 0x80)	/* CCR2.7: ECUS */
				sp->recovertime = X400P_SDL_ALARM_SETTLE_E1;
			else
				sp->recovertime = X400P_SDL_ALARM_SETTLE_SECONDS;
		}
		sp->config.ifalarms &= ~(SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC);
		sp->config.ifalarms |=
		    (alarms & (SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC));

		{
			int leds = 0, all_leds;

			/* adjust leds */
			if (alarms & SDL_ALARM_RED)
				leds |= LEDRED;
			else if (alarms & SDL_ALARM_YEL)
				leds |= LEDYEL;
			else
				leds |= LEDGRN;
			all_leds = cd->ledreg;
			all_leds &= ~(LEDYEL << (sp->span << 1));
			all_leds |= (leds << (sp->span << 1));
			if (cd->ledreg != all_leds) {
				cd->ledreg = all_leds;
				cd->xlb[LEDREG] = cd->ledreg;
			}
		}
	}
}

/** xp_e1_eval_syncsrc: - reevaluate synchronization source for card
  * @cd: card structure pointer
  *
  * There is really only one version of this function, it is just laid out in several places for
  * proper position with respect to the main interrupt service routine.
  */
static noinline fastcall __hot void
xp_e1_eval_syncsrc(struct cd *cd)
{
	/* for safety */
	if (cd->config.ifsyncsrc[0] != 0) {
		int src, synreg = SYNCSELF, ctlreg, clkreg = cd->clkreg;

		for (src = 0; src < SDL_SYNCS; src++) {
			struct sp *sp;
			int span;

			if ((span = cd->config.ifsyncsrc[src]) == 0)
				break;
			if ((--span < cd->ports && span >= 0)
			    && (sp = cd->spans[span])
			    && (sp->config.ifflags & SDL_IF_UP)
			    && (sp->config.ifflags & SDL_IF_RX_UP)
			    && (sp->config.ifflags & SDL_IF_RX_RUNNING)
			    && (sp->config.ifclock != SDL_CLOCK_LOOP)
			    && !(sp->config.ifalarms & (SDL_ALARM_BLU | SDL_ALARM_RED))) {
				synreg = cd->config.ifsyncsrc[src];
				if (sp->config.ifgtype == SDL_GTYPE_E1)
					clkreg |= E1DIV;
				else
					clkreg &= ~E1DIV;
				break;
			}
		}
		if (cd->config.ifsync != synreg) {
			cd->config.ifsync = synreg;
			ctlreg = cd->ctlreg & ~E1DIV;
			ctlreg |= clkreg;	/* use divider for span or clock */
			cd->xlb[CTLREG] = cd->ctlreg = ctlreg;
			cd->xlb[SYNREG] = cd->synreg = synreg;
		}
	}
}

/*
 *  DS21352/DS21552 Status Registers:
 *  ---------------------------------
 *  When a particular even has occurred (or is occuring), the appropriate bit in one of these
 *  registers will be set to a one.  All of the bits in SR1, SR2, RIR1, RIR2, and RIR3 registers
 *  operate in a latched fashion.  This means that if an event or an alarm occurs and a bit is set
 *  to a one in any of the registers, it will remain set until the user reads that bit.  The bill
 *  will be cleared when it is read and it will not be set again until the event has occurred again
 *  (or in the case of the RBL, RYES, LRCL, and RLOS alarms, the bit will rmain set if the alarm is
 *  still present).
 *
 *  The user will always proceed a read of any of the status registers with a write.  The byte
 *  written to the register will inform the DS21352/552 which bits the user wishes to read and have
 *  cleared.  The user will write a byte to one of these registers, with a one in the bit positions
 *  she wishes to read and a zero in the bit positions she does not wish to obtain the latest
 *  information on.  When a one is written to a bit location, the read register will be updated with
 *  the latest information.  When a zero is written to a bit position, the read register will not be
 *  updated and the previous value will be held.  A write to the status and information registers
 *  will be immediately followed by a read of the same register.  The read result should be
 *  logically AND'ed with the mask byte that was just written and this value should be written back
 *  into the same register to ensure that the bit does indeed clear.  This second write step is
 *  necessary because the alarms and events in the status register occur asynchronously in respect
 *  to their access via the parallel port.  This write-read-write scheme allows an external
 *  microcontroller or microprocessor to individually poll certain bits without disturbing the other
 *  bits in the register.  This operation is key in controlling the DS21352/552 with higher-order
 *  software languages.
 *
 *  The SR1 and SR2 registers have the unique ability to initiate a hardware interrupt via the INT
 *  output pin.  Each of the alarms and events in the SR1 and SR2 can be either masked or unmaksed
 *  from the interrupt pin via the IMR1 and IMR2 register.
 *
 *  The interrupts cause by alarms in SR1 (namely RYEL, LRCL, RBL and RLOS) act differently than the
 *  interrupts caused by events in SR1 and SR2 (namely LUP, LDN, LOTC, RSLIP, RMF, TMF, SEC, RFDL,
 *  TFDL, RMTCH, RAC and RSC).  The alarm caused interrupts will force the INT pin low whenever the
 *  alarm changes state (i.e., the alarm goes active or inactive according to the set/clear criteria
 *  in Table 7-2).  The INT pin will be allowed to return high (if no other interrrupts are present)
 *  when the user reads the alarm bit that caused the interrupt to occur even if the alarm is still
 *  present.  The event caused interrupts will force the INT pin low when the event occurs.  The INT
 *  pin will be allowed to return high (if no other interrupts are present) when the user reads the
 *  event bit that caused the interrupt to occur.
 *
 *  RIR1    (0x22)
 *  RIR1.7: COFA    Change of Frame Alignment.  Set when the last resync resulted in a change of frame
 *		    or multifram alignement.
 *  RIR1.6: 8ZD	    Eight Zero Detect.  Set when a string of at least eight consecutive zeros
 *		    (regardless of the length of the string) have been received at RPOS1 and RNEG1.
 *  RIR1.5: 16ZD    Sixteen Zero Detect.  Set when a string of at least sixteen consecutive zeros
 *		    (regardless of the length of the string) have been received at RPOS1 and RNEG1.
 *  RIR1.4: RESF    Receive Elastic Store Full.  Set when the receive elastic store buffer fills and
 *		    a frame is deleted.
 *  RIR1.3: RESE    Receive Elastic Store Empty.  Set when the receive elastic store buffer empties
 *		    and a frame is repeated.
 *  RIR1.2: SEFE    Severetly Error Framing Event.  Set when 2 our of 6 framing bits (Ft or FPS) are
 *		    received in error.
 *  RIR1.1: B8ZS    B8ZS Code Word Detect.  Set when a B8ZS code word is detected at RPOS1 and RNEG1
 *		    independent of whether the B8ZS mode is selected or not via CCR2.6.  Useful for
 *		    automatically setting the line coding.
 *  RIR1.0: FBE	    Frame Bit Error.  Set when a Ft (D4) or FPS (ESF) framing bit is received in
 *		    error.
 *
 *  RIR2    (0x31)
 *  RIR2.7: RLOSC   Receive Loss of Sync Clear. Set when the framer acheives syncrhonization; will
 *		    remain set until read.
 *  RIR2.6: LRCLC   Line Interface Receive Carrier Loss Clear.  Set when the carrier signal is
 *		    restored; will remain set until read.  See Table 7-2.
 *  RIR2.5: TESF    Transmit Elastic Store Full.  Set when the transmit elastic store buffer fills
 *		    and a frame is deleted.
 *  RIR2.4: TESE    Transmit Elastic Store Empty.  Set hwen the transmit elastic store buffer
 *		    empties and a frame is repeated.
 *  RIR2.3: TSLIP   Transmit Elastic Store Slip Occurence.  Set when the transmit elastic store has
 *		    either repeated or deleted a frame.
 *  RIR2.2: RBLC    Receive Blue Alarm Clear.  Set when the Blue Alarm (AIS) is no longer detected;
 *		    will remain set until read.  See Table 7-2.
 *  RIR2.1: RPDV    Receive Pulse Density Violation.  Set when the receive data stream does not meet
 *		    the ANSI T1.403 requirements for pulse density.
 *  RIR2.0: TPDV    Transmit Pulse Density Violation.  Set when the transmit data stream does not
 *		    meet the ANSI T1.403 requirements for pulse density.
 *
 *  RIR3    (0x10)
 *  RIR3.7: RL1	    Receive Level Bit 1.
 *  RIR3.6: RL0	    Receive Level Bit 0.
 *		    '00'B +2dB   to -7.5dB
 *		    '01'B -7.4dB to -15dB
 *		    '10'B -15dB  to -22.5dB
 *		    '11'B less than -22.5dB
 *  RIR3.5: JALT    Jitter Attenuation Trip Limit.  Set when the jitter attenuator FIFO reachs to
 *		    within 4 bits of its limit; useful for debugging jitter attenuation operation.
 *  RIR3.4: LORC    Loss of Receive Clock.  Set when the RCLKI pin has not transitioned for at least
 *		    2 us (4 us max).
 *  RIR3.3: FRCL    Framer Receive Carrier Loss.  Set when 192 consecutive zeros have been received
 *		    at the RPOSI and RNEGI pins; allowed to be cleared when 14 or more ones out of
 *		    112 possible bit positions are received.
 *  RIR3.2: N/A
 *  RIR3.1: N/A
 *  RIR3.0: N/A
 *
 *  SR1	    (0x20)
 *  SR1.7:  LUP	    Loop Up Code Detected.  Set when the loop up code as defined in the RUPCD
 *		    register is being received.  See section 16.5 for details.
 *  SR1.6:  LDN	    Loop Down Code Detected.  Set when the loop down code as defined in the RDNCD
 *		    register is being received.  See section 16.5 for details.
 *  SR1.5:  LOTC    Loss of Transmit Clock.  Set when the TCLK pin has not transitioned for one
 *		    channel time (or 5.2 us).  Will force RLOS/LOTC pin high if enabled via CCR1.6.
 *		    Also will force transmit side formatter to switch to RCLK if so enabled with
 *		    TCR1.7.
 *  SR1.4:  RSLIP   Receive Elastic Store Slip Occurrence.  Set when the receive elastic store has
 *		    either repeated or deleted a frame.
 *  SR1.3:  RBL	    Receive Blue Alarm.  Set when an unframed all one's code is received at RPOSI
 *		    and RNEGI.
 *  SR1.2:  RYEL    Receive Yellow Alarm.  Set when a yellow alarm is received at RPOSI and RNEGI.
 *  SR1.1:  LRCL    Line Interface Receive Carrier Loss.  Set when a red alarm is received at RTIP
 *		    and RRING.
 *  SR1.0:  RLOS    Receive Loss of Sync.  Set when the device is not synchronized to the receive T1
 *		    stream.
 *
 *  SR2	    (0x21)
 *  SR2.7:  RMF	    Receive Multiframe.  Set on receive multiframe boundaries.
 *  SR2.6:  TMF	    Transmit Multiframe.  Set on transmit multiframe boundaries.
 *  SR2.5:  SEC	    One Second Timer.  Set on increments of one second based on RCLK; will be set in
 *		    increments of 999ms, 999ms, and 1002ms every 3 seconds.
 *  SR2.4:  RFDL    Receive FDL Buffer Full.	Set when the receive FDL buffer (RFDL) fills to
 *		    capacity (8 bits).
 *  SR2.3:  TFDL    Transmit FDL Buffer Empty.  We then the transmit FDL buffer (TFDL) empties.
 *  SR2.2:  RMTCH   Receive FDL Match Occurence.  Set when the RFDL matches either RFDLM1 or RFDLM2.
 *  SR2.1:  RAF	    Receive FDL Abort.  Set when eight conecutive one's are received in the FDL.
 *  SR2.0:  RSC	    Receive Signaling Change.  Set when the DS21352/552 detects a change of stat in
 *		    any of the robbed-bit signalling bits.
 *
 *  IMR1    (0x7f)
 *  IMR1.7: LUP	    SR1.7
 *  IMR1.6: LDN	    SR1.6
 *  IMR1.5: LOTC    SR1.5
 *  IMR1.4: RSLIP   SR1.4
 *  IMR1.3: RBL	    SR1.3
 *  IMR1.2: RYEL    SR1.2
 *  IMR1.1: LRCL    SR1.1
 *  IMR1.0: RLOS    SR1.0
 *
 *  IMR2    (0x6f)
 *  IMR2.7: RMF	    SR2.7
 *  IMR2.6: TMF	    SR2.6
 *  IMR2.5: SEC	    SR2.5
 *  IMR2.4: RFDL    SR2.4
 *  IMR2.3: TFDL    SR2.3
 *  IMR2.2: RMTCH   SR2.2
 *  IMR2.1: RAF	    SR2.1
 *  IMR2.0: RSC	    SR2.0
 *
 */

static noinline fastcall __hot void xp_x1_txrx_burst(struct cd *cd);
static noinline fastcall __hot void xp_x1_process_span(struct cd *cd, struct sp *sp,
						       register volatile uint8_t *xlb, register uint ints);
static noinline fastcall __hot void xp_x1_process_timeout(struct cd *cd, struct sp *sp, volatile uint8_t *xlb,
							  register uint flags);
static noinline fastcall __hot void xp_x1_process_state(struct cd *cd, struct sp *sp, volatile uint8_t *xlb,
							const register uint flags,
							const register uint errors);
static noinline fastcall __hot void xp_x1_process_stats(struct sp *sp, register uint errors);
static noinline fastcall __hot void xp_x1_process_alarms(struct cd *cd, struct sp *sp,
							 register volatile uint8_t *xlb, register uint flags,
							 uint timeout);
static noinline fastcall __hot void xp_x1_eval_syncsrc(struct cd *cd);
static noinline fastcall __unlikely void xp_x1_process_auto(struct cd *cd, struct sp *sp,
							    register volatile uint8_t *xlb,
							    register uint flags, const register uint errors,
							    const uint timeout);
noinline __unlikely int xp_x1_change_config(struct sp *sp, register volatile uint8_t *xlb);
noinline __unlikely int xp_x1_change_gtype(struct sp *sp, register volatile uint8_t *xlb);

/** xp_x1_interrupt: - process a TE400 interrupt
  * @irq: interrupt number
  * @dev_id: card structure pointer (opaque)
  * @regs: interrupted registers
  *
  * The user always precedes a read of any of the status registers with a write.  The byte written
  * to the register informs the DS2155 which bits the user wishes to read and have cleared.  The
  * user writes a byte to one of these registers, with a 1 in the bit potions the user wishes to
  * reate and a 0 in the bit positions the user does not wish to obtain the latest information on.
  * When a 1 is written to a bit location, the read register is updated with the latest information.
  * When a 0 is written to a bit position, the read register is not updated and the previous value
  * is held.  A write to the status registers is immediately followed by a read of the same
  * register.  This read-write scheme allows an external microcontroller or microprocessor to
  * individually poll certain bits without disturbing the other bits in the register.  This
  * operation is key in controlling the DS2155 with higher order languages.  
  *
  * Process an interrupt for a TE400 card (with a DS21Q55, DS21455 or DS21458 chip).  The spans on
  * this card can be either E1 or T1/J1 spans.  This is a departure from the previous driver which
  * counted frames to determine timeout events: here we utilize the Dallas interrupts available on
  * the card for non-frame events.  This function should really be aligned on a page boundary and
  * the functions that it calls laid out immediately following the function, thus the forward
  * declarations above.
  */
STATIC __hot irqreturn_t
#ifdef HAVE_KTYPE_IRQ_HANDLER_2ARGS
xp_x1_interrupt(int irq, void *dev_id)
#else
xp_x1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
	static unsigned long lasttime = 0;
	struct cd *cd = (struct cd *) dev_id;
	int safety;

	/* Keep another processor from entering the interrupt while another is already processing the
	   interrupt.  This means that the top half of the driver must disable interrupts when taking the
	   card lock to avoid deadly embrace. */
	spin_lock(&cd->lock);
	/* active interrupt (otherwise, supurios or shared) */
	if (!(cd->xlb[STAREG] & (INTACTIVE | DINTACT))) {
		spin_unlock(&cd->lock);
		return (irqreturn_t) (IRQ_NONE);
	}
	if (likely(cd->xlb[STAREG] & INTACTIVE)) {
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | OUTBIT | DINTENA | INTACK);
		xp_x1_txrx_burst(cd);
		cd->xlb[CTLREG] = cd->ctlreg | (INTENA | DINTENA);
	}
	for (safety = 0; unlikely(cd->xlb[STAREG] & DINTACT); safety++) {
		uint span;
		struct sp *sp;

		/* Try to keep this burning a loop here when there is only one span that is responsible for
		   the Dallas interrupt. */
		for (span = 0; span < cd->ports; span++)
			if (likely((sp = cd->spans[span]) != NULL)) {
				register volatile uint8_t *xlb = (typeof(xlb)) sp->iobase;
				register uint ints;

				xlb[0x14] = 0x1f;	/* SR1 thru SR5 */
				if (likely((ints = xlb[0x14] & 0x1f) != 0))
					xp_x1_process_span(cd, sp, xlb, ints);
			}
		if (unlikely(safety > 8)) {
			/* throttle software error message */
			if (lasttime == 0 || (jiffies - lasttime) > HZ) {
				lasttime = jiffies;
				swerr();
			}
			break;
		}
	}
	/* Reevaluate the sync source when necessary.  Try to skip the call here whenever possible to keep
	   the ISR tight. */
	if (unlikely(xchg((int *) &cd->eval_syncsrc, 0) != 0)) {
		if (cd->config.ifsyncsrc[0] != 0)
			xp_x1_eval_syncsrc(cd);
	}
	spin_unlock(&cd->lock);
	return (irqreturn_t) (IRQ_HANDLED);
}

/** xp_x1_txrx_burst: - burst transfer TX and RX data
  * @cd: card structure pointer
  *
  * This function is responsible for transfering frame data to and from the card.  This is being
  * done with host driven I/O because the PLX9030 is incapable of bus mastering DMA.  When the
  * memory mapped I/O region is properly described, PCI burst transfers will occur.  Transfers are
  * 1024 bytes written and 1024 bytes read.  These are interleaved, but might better be performed as
  * 4 Lword interleaved transfers.
  *
  * This function is identical to xp_e1_txrx_burst(), but is laid out again here so that it is in
  * the correct location w.r.t. the main interrupt routine.
  */
static noinline fastcall __hot void
xp_x1_txrx_burst(struct cd *cd)
{
	int lebno;

	if ((lebno = (cd->lebno + 1) & (X400P_EBUFNO - 1)) != cd->uebno) {
		register int slot;
		register volatile uint32_t *xll;
		register const uint32_t *wbuf = cd->wbuf + (lebno << 8);
		register const uint32_t *const wend = wbuf + 256;
		register uint32_t *rbuf = cd->rbuf + (lebno << 8);

		cd->lebno = lebno;
		for (xll = cd->xll; wbuf < wend;)
			for (wbuf++, rbuf++, xll++, slot = 1; slot < 32; slot++, xll++, wbuf++, rbuf++) {
				prefetch(wbuf + 1);
				prefetchw(rbuf + 1);
				*xll = *wbuf;
				*rbuf = *xll;
			}
		tasklet_hi_schedule(&cd->tasklet);
	} else
		xp_overflow(cd);
}

/** xp_x1_process_span: - process Dallas interrupts for a single span
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: Dallas chip address space
  * @ints: active interrupt status register mask
  *
  * PERFORMANCE DEFECTS:
  *
  * XP_DEF_AIS: Alarm Indication Signal (AIS) Defect:
  *	For D4 and ESF links, the 'all ones' condition is detected at a DS1 line interface upon
  *	observing an unframed signal with a one's density of at least 99.9 percent present for a
  *	time equal to or greater than T, where 3ms is less than or equal to T, which is less than or
  *	equal to 75ms.  The AIS is terminated upon observing a signal not meeting the one's density
  *	of the unframed signal criteria for a period equal to or greater than T.  For E1 links, the
  *	'all-ones' condition is detected at the line interface as a string of 512 bits containing
  *	fewer than three zero bits. 
  *
  * XP_DEF_OOF: Out of Frame (OOF) Defect:
  *	An OOF defect is the occurrence of a particular density of Framing Error events.  For T1
  *	links, an OOF defect is declared when the receiver detects two or more framing errors within
  *	a 3 ms period for ESF signals and 0.75 ms for D4 signals, or two or more errors out of five,
  *	or fewer consecutive framing-bits.  For E1 links, an OOF defect is declared when three
  *	consecutive frame alignment signals have been received with an error.  When an OOF defect is
  *	declared, the frame starts searching for a correct pattern.  The OOF defect ends when the
  *	signal is in-frame.  In-frame occurs when there are fewer than two frame bit errors within a
  *	3 ms period for ESF signals and 0.75 ms for D4 signals.  For E1 links, in-frame occurs when
  *	in frame N, the frame alignment signal is correct; and, in frame N+1, the frame alignment
  *	signal is absent (that is, bit 2 in TS0 is set to one); and, in frame N+2, the frame
  *	alignment signal is present and correct. 
  *
  * FAILURE STATES: The following failure states are received or detected failures that are
  * reported.  The conditions under which a DS1 interface would, if ever, produce the conditions
  * leading to the failure state are described in the appropriate specification.
  *
  * XP_FAIL_AIS: Alarm Indication Signal (AIS) Failure:
  *	The AIS failure is declared when an AIS defect is detected at the input and the AIS defect
  *	still exists after the LOF failure (which is caused by the unframed nature of the all-ones
  *	signal) is detected.  The AIS failure is cleared when the LOF failure is cleared.  
  *
  * XP_FAIL_FEA: Far End Alarm Failure (Yellow Alarm):
  *	The Far End Alarm failure is also known as Yellow Alarm in the T1 cases and Distant Alarm
  *	(or RAI) in the E1 case.  For D4 links, the FEA failure is declared when bit 6 of all
  *	channels have been zero for at least 335 ms and is cleared when bit 6 of at least one
  *	channel is non-zero for a period T, where T is usually less than one second and alway less
  *	than five seconds.  The FEA failure is not declared for D4 links when LOS is detected.  For
  *	ESF links, the FEA failure is declared if the Yellow Alarm signal pattern occurs in a least
  *	seven out of ten contiguous 16-bit-pattern intervals and is cleared if the Yellow Alarm
  *	signal pattern does not occur in ten contiguous 16-bit signal pattern intervals.  For E1
  *	links, the FEA failure is declared when bit 3 of time-slot zero is set to one on two
  *	consecutive occasions.  The FEA failure is cleared when bit 3 of time-slot zero is received
  *	set to zero.  
  *
  * XP_FAIL_FELOM: Far End Loss of Multiframe Failure:
  *	The FELOM failure is declared when bit 2 of TS16 of frame 0 is received set to one on two
  *	consecutive occasions.  The FELOM failure is cleared when bit 2 of TS16 of frame 0 is
  *	received set to zero.  The FELOM failure can only be declared for E1 links operating in CAS
  *	mode.  
  *
  * XP_FAIL_LPF: Loopback Pseudo-Failure:
  *	The LPF is declared when the near end equipment has placed a loop-back (of any kind) on the
  *	DS1.  This allows a management entity to determine from one object whether the DS1 can be
  *	considered to be in service or not (from the point of view of the near-end equipment).  
  *
  * XP_FAIL_LOF: Loss of Frame (LOF) Failure:
  *	For T1 links, the LOF failure is declared with an OOF or LOS defect has persisted for T
  *	seconds, where T is greater than or equal to two, but less than or equal to ten.  The LOF
  *	failure is cleared when there have been no OOF or LOS defects during a period T is greater
  *	than or equal to two, but less than or equal to twenty.  Many systems will perform "hit
  *	integration" with the period T before declaring or clearing the failure. 
  *
  * XP_FAIL_LOM: Loss of Multiframe (LOM) Failure:
  *	The LOM failure is declared when two consecutive multi-frame alignment signals (bit 4
  *	through 7 of TS16 of frame 0) have been received wtih an error.  The LOM failure is cleared
  *	when the first correct multi-frame alignment signal is received.  The LOM failure can only
  *	be declared for E1 links operating with framing (sometimes called CAS mode).  
  *
  * XP_FAIL_LOS: Loss of Signal (LOS) Failure:
  *	For T1, the Loss of Signal failure is declared upon observing 175 +/- 74 contiguous pulse
  *	position with no pulses of either positive or negative polarity.  The LOS failure is cleared
  *	upon observing an average pulse density of at least 12.5 percent over a period of 175 +/- 74
  *	contiguous pulse positions starting with the receipt of a pulse.  
  *
  * XP_FAIL_T16AIS: T16 Alarm Indication Signal Failure:
  *	For E1 links, the Ts16 Alarm Indication Signal failure is declared when time-slot 16 is
  *	received as all ones for all frames of two consecutive multi-frames.  This condition is
  *	never declared for T1.  
  *
  */
static noinline fastcall __hot void
xp_x1_process_span(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint ints)
{
	register uint8_t status, mask;
	register uint errors = sp->status.errors;
	register uint flags = sp->status.flags;
	uint timeout = 0;

	if (ints & (1 << 0)) {	/* SR1 */
		/* only examine bits set for interrupt */
		if ((mask = xlb[0x17])) {	/* IMR1 */
			/* write-read cycle */
			xlb[0x16] = mask;
			status = xlb[0x16] & mask;
			/* SR1.7: ILUT: Input Level Under Threshold.  This bit is set whenever the input
			   level at RTIP and RRING falls below the threshold set by the value in CCR4.4
			   thorugh CCR4.7.  The level must remain below the programmed threshold for
			   approximately 50ms for this bit to be set. This is a double interrupt bit. */
			/* 0000 > -2.5dB, 00 = 0dB boost */
			/* 0001 = -2.5dB */
			/* 0010 = -5.0dB */
			/* 0011 = -7.5dB */
			/* 0100 = -10.0dB */
			/* 0101 = -12.5dB */
			/* 0110 = -15.0dB */
			/* 0111 = -17.5dB */
			/* 1000 = -20.0dB, 01 = 20dB boost */
			/* 1001 = -22.5dB */
			/* 1010 = -25.0dB */
			/* 1011 = -27.5dB, 10 = 26dB boost */
			/* 1100 = -30.0dB */
			/* 1101 = -32.5dB, 11 = 32dB boost */
			/* 1110 = -35.0dB */
			/* 1111 < -37.5dB */
			if (status & (1 << 7)) {
				flags |= XP_STATE_ILUT;
			} else if (mask & (1 << 7)) {
				flags &= ~XP_STATE_ILUT;
			}
			/* SR1.6: TIMER: Timer.  Follows the error counter update interval as determined by
			   the ECUS bit in the error counter configuration register (ERCNT).  T1 mode: set on 
			   increments of one second or 42ms based on RCLK. E1 mode: set on increments of one
			   second or 62.5ms based on RCLK. */
			if (status & (1 << 6)) {
				timeout = 1;
			}
			/* SR1.5: RSCOS: Receive Sig. Change-of-State.  Set when any channel selected by the
			   receive signalling change of state interrupt-enable registers (RSCSE1 through
			   RSCSE4) changes signalling state.  */
			if (status & (1 << 5)) {
			}
			/* SR1.4: JALT: Jitter Attenuator Limit Trip.  Set when the jitter attenuator FIFO
			   reaches to within 4 bits of its useful limit.  Will be cleared when read.  Useful
			   for debugging jitter-attenuator operation. */
			if (status & (1 << 4)) {
				/* The jitter attenuator trip limit can indicate a wrong E1/T1 setting. */
				if (flags & SDL_IF_AUTOCONFIG) {
					switch (sp->config.ifgtype) {
					case SDL_GTYPE_E1:
						/* ETSI was not specified when the driver loaded, but that is 
						   the current setting, it should be T1.  Reconfigure the
						   span for T1 operation and do a LIRST.  */
						if (!etsi) {
						}
						break;
					case SDL_GTYPE_T1:
						/* ANSI was not specified when the driver loaded, but that is 
						   the current setting, it should be E1.  Reconfigure the
						   span for E1 operation and do a LIRST.  */
						if (!ansi) {
						}
						break;
					case SDL_GTYPE_J1:
						/* JAPAN was not specified when the driver loaded, but that
						   is the current setting, it should be E1.  Reconfigure the
						   span for E1 operation and do a LIRST.  */
						if (!japan) {
						}
						break;
					}
				}
			}
			/* SR1.3: LRCL: Line Interface Receive Carrier-Loss.  Set when the carrier signal is
			   lost. */
			if (status & (1 << 3)) {
				flags |= XP_STATE_LRCL;
			} else if (mask & (1 << 2)) {
				flags &= ~XP_STATE_LRCL;
			}
			/* SR1.2: TCLE: Transmit Current-Limit Exceeded.  Set when the 50mA (RMS) current
			   limiter is activate whether the current limiter is enabled or not. */
			if (status & (1 << 2)) {
				flags |= XP_STATE_TSCD;
			} else if (mask & (1 << 2)) {
				flags &= ~XP_STATE_TSCD;
			}
			/* SR1.1: TOCD: Transmit Open-Circuit Detect.  Set when the device detects that TTIP
			   and TRING outputs are open-circuited. */
			if (status & (1 << 1)) {
				flags |= XP_STATE_TOCD;
			} else if (mask & (1 << 1)) {
				flags &= ~XP_STATE_TOCD;
			}
			/* SR1.0: LOLITC: Loss of Line-Interface Transmit Clock.  Set when TCLKI has not
			   transitioned for one channel time.  */
			if (status & (1 << 0)) {
				flags |= XP_STATE_LOLITC;
			} else if (mask & (1 << 0)) {
				flags &= ~XP_STATE_LOLITC;
			}
		}
	}
	if (ints & (1 << 1)) {	/* SR2 */
		/* only examine bits set for interrupt */
		if ((mask = xlb[0x19])) {	/* IMR2 */
			/* write-read cycle */
			xlb[0x18] = mask;
			if ((status = xlb[0x18] & mask)) {
				/* SR2.7: RYELC: Receive Yellow Alarm Clear (T1) event. Set when the yellow
				   alarm condition is no longer detected.  */
				if (status & (1 << 7)) {
					flags &= ~XP_STATE_RYEL;
				}
				/* SR2.6: RUA1C: Receive Unframed All-Ones Clear event. Set when the unframed 
				   all ones condition is no longer detected.  */
				if (status & (1 << 6)) {
					flags &= ~XP_STATE_RUA1;
				}
				/* SR2.5: FRCLC: Framer Receive Carrier-Loss Clear event. Set when carrier
				   loss condition at RPOSI and RNEGI is no longer detected. */
				if (status & (1 << 5)) {
					flags &= ~XP_STATE_FRCL;
				}
				/* SR2.4: RLOSC: Receive Loss-of-Sync Clear event. Set when the framer
				   achieves synchronization; will remain set until read. */
				if (status & (1 << 4)) {
					flags &= ~XP_STATE_RLOS;
				}
				/* SR2.3: RYEL: Receive Yellow Alarm (T1) condition.  Set when a yellow alarm 
				   is received at RPOSI and RNEGI.  */
				if (status & (1 << 3)) {
					flags |= XP_STATE_RYEL;
				}
				/* SR2.2: RUA1: Receive Unframmed All-Ones condition. Set when an unframed
				   all ones code is received at RPOSI and RNEGI.  */
				if (status & (1 << 2)) {
					flags |= XP_STATE_RUA1;
				}
				/* SR2.1: FRCL: Framer Receive Carrier-Loss condition. Set when 255 (or 2048
				   if E1RCR2.0=1) E1 mode or 192 T1 mode consecutive zeros have been detected
				   at RPOSI and RNEGI.  */
				if (status & (1 << 1)) {
					flags |= XP_STATE_FRCL;
				}
				/* SR2.0: RLOS: Receive Loss-of-Sync condition.  Set when the device is not
				   synchronized to the received data stream.  */
				if (status & (1 << 0)) {
					flags |= XP_STATE_RLOS;
				}
			}
		}
	}
	if (ints & (1 << 2)) {	/* SR3 */
		/* only examine bits set for interrupt */
		if ((mask = xlb[0x1b])) {	/* IMR3 */
			/* write-read cycle */
			xlb[0x1a] = mask;
			status = xlb[0x1a] & mask;
			/* SR3.7: LSP: Spare Code Detected (T1).  Set when the spare code defined in the
			   RSCD1/2 registers is being received.  */
			if (status & (1 << 7)) {
				flags |= XP_STATE_SPARE;
			} else if (mask & (1 << 7)) {
				flags &= ~XP_STATE_SPARE;
			}
			/* SR3.6: LDN: Loop-Down Code Detected (T1).  Set when the loop down code as defined
			   in the RDNCD1/2 regsiter is being received.  */
			if (status & (1 << 6)) {
				flags |= XP_STATE_LDN;
				if (!(sp->config.ifgmode & SDL_GMODE_LOC_LB)) {
					if ((sp->config.ifmode & SDL_GMODE_REM_LB)) {
						if (sp->loopcnt > 80) {
							/* LCBR.2: RLB: 0 = no remote */
							xlb[0x4a] &= ~0x04;
							sp->config.ifgmode &= ~SDL_GMODE_REM_LB;
							sp->loopcnt = 0;
						} else
							sp->loopcnt++;
					}
				}
			} else if (mask & (1 << 6)) {
				flags &= ~XP_STATE_LDN;
			}
			/* SR3.5: LUP: Loop-Up Code Detected (T1).  Set when the loop up code as defined in
			   the RUPCD1/2 register is being received. */
			if (status & (1 << 5)) {
				flags |= XP_STATE_LUP;
				if (!(sp->config.ifgmode & SDL_GMODE_LOC_LB)) {
					if (!(sp->config.ifgmode & SDL_GMODE_REM_LB)) {
						if (sp->loopcnt > 80) {
							/* LCBR.2: RLB: 1 = remote */
							xlb[0x4a] |= 0x04;
							sp->config.ifgmode |= SDL_GMODE_REM_LB;
							sp->loopcnt = 0;
						} else
							sp->loopcnt++;
					}
				}
			} else if (mask & (1 << 5)) {
				flags &= ~XP_STATE_LUP;
			}
			/* SR3.4: LOTC: Loss of Transmit Clock.  Set when the TCLK pin has not transitioned
			   for one channel time.  WIll force the LOTC pin high if enabled via CCR1.0. */
			if (status & (1 << 4)) {
				flags |= XP_STATE_LOTC;
			} else if (mask & (1 << 4)) {
				flags &= ~XP_STATE_LOTC;
			}
			/* SR3.3: LORC: Loss of Receive Clock.  Set when the RCLKI pin has not transitioned
			   for once channel time. */
			if (status & (1 << 3)) {
				flags |= XP_STATE_LORC;
			} else if (mask & (1 << 3)) {
				flags &= ~XP_STATE_LORC;
			}
			/* SR3.2: V52: V5.2 Link Detect (E1).  Set on detection of a V5.2 link identification 
			   signal. */
			if (status & (1 << 2)) {
			}
			/* SR3.1: RDMA: Receive Distant MF Alarm (E1).  Set when bit 6 of time slot 16 in
			   frame 0 has been set for two consecutive multiframes.  This alarm is not disabled
			   in the CCS signaling mode.  Note that we disable this interrupt when not E1-CAS. */
			if (status & (1 << 1)) {
				flags |= XP_STATE_RDMA;
				/* Note that when we are in CCS signalling mode and we receive this alarm
				   there is the possibility that we should have set CAS. */
			} else if (mask & (1 << 1)) {
				flags &= ~XP_STATE_RDMA;
			}
			/* SR3.0: RRA: Receive Remote Alarm (E1).  Set when a remote alarm is received at
			   RPOSI and RNEGI.  */
			if (mask & (1 << 0)) {
				if (status & (1 << 0)) {
					flags |= XP_STATE_RRA;
				} else {
					flags &= ~XP_STATE_RRA;
				}
			}
		}
	}
	if (ints & (1 << 3)) {	/* SR4 */
		/* only examine bits set for interrupt */
		/* Note: we are currently setting IMR4 to 10000000 for T1/J1 and 01100000 for E1 */
		if ((mask = xlb[0x1d])) {	/* IMR4 */
			/* write-read cycle */
			xlb[0x1c] = mask;
			status = xlb[0x1c] & mask;
			/* SR4.7: RAIS: Recevie AIS-CI (T1).  Set when the receiver detects the AIS-CI
			   pattern as deifned in ANSI T1.403. */
			if (status & (1 << 7)) {
				flags |= XP_STATE_RAIS;
			} else if (mask & (1 << 7)) {
				flags &= ~XP_STATE_RAIS;
			}
			/* SR4.6: RSAO: Receive Sig. All-Ones (E1).  Set when the contents of time slot 16
			   contains fewer than three zeroes over 16 consecutive frames. This alarm is not
			   disabled in the CCS signalling mode.  */
			if (status & (1 << 6)) {
				flags |= XP_STATE_RSAO;
				/* Note that when we are in the CAS signalling mode and we receive this
				   condition there is the possibilty that we should be in the CCS signalling
				   mode because we appear to be receiving idle code in channel 16. */
				if (sp->config.ifframing == SDL_FRAMING_CAS) {
					if (sp->config.ifflags & SDL_IF_AUTOCONFIG) {
						sp->config.ifframing = SDL_FRAMING_CCS;
						/* SIGCR.1: TCCS: 1 = signalling data in CCS format */
						/* SIGCR.2: RCCS: 1 = signalling data in CCS format */
						xlb[0x40] |= 0x06;
						xlb[0x33] |= 0x40;
					}
				}
			} else if (mask & (1 << 6)) {
				flags &= ~XP_STATE_RSAO;
			}
			/* SR4.5: RSAZ: Receive Sig. All-Zeros (E1).  Set when over a full MF time slot 16
			   contains all zeros.  */
			/* Set when over a full MF time slot 16 contains all zeros. */
			if (status & (1 << 5)) {
				flags |= XP_STATE_RSAZ;
			} else if (mask & (1 << 5)) {
				flags &= ~XP_STATE_RSAZ;
			}
#if 0
			/* SR4.4: TMF: Transmit Multiframe.  E1 Mode: Set every 2ms (regardless if CRC-4 is
			   enabled) on transmit multiframe boundaries.  Use to alert the host that signaling
			   data needs to be updated.  T1 Mode: Set every 1.5ms on D4 MF boundaries or every
			   3ms on ESF MF boundaries. */
			if (status & (1 << 4)) {
			}
			/* SR4.3: TAF: Transmit Align Frame (E1).  Set every 250us at the beginning of align
			   frames. Used to alert the host that the TAF and TNAF registers need to be updated. 
			 */
			if (status & (1 << 3)) {
			}
			/* SR4.2: RMF: Receive Multiframce.  E1 Mode: Set every 2ms (regardless if CAS
			   signalling is enabled or not) on the receive multiframe boundaries. Use to alert
			   the host that signaling data is available.  T1 Mode: Set every 1.5ms on D4 MF
			   boundaries or every 3ms on ESF MF boundaries.  */
			if (status & (1 << 2)) {
			}
			/* SR4.1: RCMF: Receive CRC4 Multiframe (E1).  Set on CRC-4 multiframe boundaries;
			   will continue to be set every 2ms on an arbitrary boundary if CRC-4 is disabled.  */
			if (status & (1 << 1)) {
			}
			/* SR4.0: RAF: Receive Align Frame (E1).  Sett every 250us at the beginning of align
			   frames. Used to alter the host that Si and Sa bits are available in the RAF and
			   RNAF registers.  */
			if (status & (1 << 0)) {
			}
#endif
		}
	}
	if (ints & (1 << 4)) {	/* SR5 (Elastic Store) */
		/* only examine bits set for interrupt */
		if ((mask = xlb[0x1f])) {	/* IMR5 */
			/* write-read cycle */
			xlb[0x1e] = mask;
			if ((status = xlb[0x1e] & mask)) {
				/* SR5.5: TESF: Transmit Elastic Store Full Event. Set when the transmit
				   elastic store buffer fills and a frame is deleted.  */
				if (status & (1 << 5)) {
					errors |= XP_ERR_CSS;
				}
				/* SR5.4: TESEM: Transmit Elastic Store Empty Event. Set when the transmit
				   elastic store buffer emtpies and a frame is repeated. */
				if (status & (1 << 4)) {
					errors |= XP_ERR_CSS;
				}
				/* SR5.3: TSLIP: Transmit Elastic Store Slip-Occurrence Event.  Set when the
				   transmit elastic sotre has either repeated or deleted a frame.  */
				if (status & (1 << 3)) {
					errors |= XP_ERR_CSS;
				}
				/* SR5.2: RESF: Receive Elastic Store Full Event. Set when the receive
				   elastic store buffer filles and a frame is deleted.  */
				if (status & (1 << 2)) {
					errors |= XP_ERR_CSS;
				}
				/* SR5.1: RESEM: Receive Elastic Store Empty Event. Set when the receive
				   elastic store buffer empties and a frame is repeated. */
				if (status & (1 << 1)) {
					errors |= XP_ERR_CSS;
				}
				/* SR5.0: RSLIP: Receive Elastic Store Slip-Occurrence Event.  Set when the
				   receive eleastic store has either repeated or deleted a frame. */
				if (status & (1 << 0)) {
					errors |= XP_ERR_CSS;
				}
			}
		}
	}

	/* Note: at this point, any Dallas interrupt should have been cleared by reading the interrupt bits.
	   Dallas interrupts generated by the IMR registers are acknowledged by reading the corresponding bit 
	   of the SR registers. */

	if (timeout)
		xp_x1_process_timeout(cd, sp, xlb, flags);

	/* Note that alarms are processed asynchronously whereas interface state is only processed during
	   timeouts. This keeps the interface state from thrashing around and detection mechanisms falsely
	   triggering on transient conditions. */
	if ((flags ^ sp->status.flags) != 0)
		xp_x1_process_alarms(cd, sp, xlb, flags, timeout);
}

/** xp_x1_process_timeout: - process timeout for a span
  * @cd: card structure (locked)
  * @sp: span structure
  * @xlb: span iobase pointer
  * @flags: span flags
  */
static noinline fastcall __hot void
xp_x1_process_timeout(struct cd *cd, struct sp *sp, volatile uint8_t *xlb, register uint flags)
{
	register uint mask, status;
	register uint errors = sp->status.errors;
	uint count;

	/* Read information registers and report. */
	/* write-read cycle */
	mask = 0x3f;
	xlb[0x11] = mask;
	status = xlb[0x11] & mask;
	sp->config.ifrxlevel = (status & 0x0f) / 3;
	if (status & 0x30) {
		/* INFO2.4: TOCD: Transmit Open-Circuit Detect.  A real-time bit set when the device detects
		   that the TTIP and TRING outputs are open-circuited. */
		if (status & (1 << 4)) {
			flags |= XP_STATE_TOCD;
		} else if (mask & (1 << 4)) {
			flags &= ~XP_STATE_TOCD;
		}
		/* INFO2.5: TCLE: Transmit Current-Limit Exceeded.  A real-time bit set when the 50mA (RMS)
		   current limiter is activated, whether the current limiter is enabled or not.  */

		if (status & (1 << 5)) {
			flags |= XP_STATE_TSCD;
		} else if (mask & (1 << 5)) {
			flags &= ~XP_STATE_TSCD;
		}
		/* INFO2.6: BD: BOC Detected.  A real-time bit that is set high when the BOC detector is
		   presently seeing a valid sequence and set low when no BOC is currently being detected. */
		if (status & (1 << 6)) {
		}
		/* INFO2.7: BSYNC: BERT Real-Time Synchronization Status.  Real-time status of the
		   synchronizer (this bit is not latched).  Will be set when the incoming pattern matches for 
		   32 consecutive bit positions. Will be cleared when six or more bits out of 64 are received 
		   in error. Refer to BSYNC in the BERT status register, SR9, for an interrupt-generating
		   version of this signal. */
		if (status & (1 << 7)) {
		}
	}
	switch (__builtin_expect(sp->config.ifgtype, SDL_GTYPE_E1)) {
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		/* T1/J1-only register */
		/* write-read cycle */
		mask = 0xff;
		xlb[0x10] = mask;
		status = xlb[0x10] & mask;
		if (status) {
			/* INFO1.0: FBE: Frame Bit-Error Event.  Set when a Ft(D4) or FPS(ESF) framing bit is 
			   received in error.  Note that these can be counted by the MOSCR error counter. */
			if (status & (1 << 0)) {
				errors |= XP_ERR_FASE;
			}
			/* INFO1.1: B8ZS: B8ZS Codeword Detect Event.  Set when a B8SZ codeword is detected
			   at RPOS and RNEG independent of whether the B8ZS mode is selected or not via
			   T1TCR2.7.  Useful for automatically setting the line coding. */
			if (status & (1 << 1)) {
				/* A B8ZS codeword was detected.  When we are set to T1 AMI and there are no
				   bipolar violations, and a B8ZS codeword is detected, this certainly means
				   that the line coding should be B8ZS instead of AMI.  However, SS7 links
				   are always B8ZS.  */
				if (sp->config.ifcoding != SDL_CODING_B8ZS)
					errors |= XP_ERR_B8ZS;
			}
			/* INFO1.2: SEFE: Severly Errored Framing Event.  Set when two out of six framing
			   bits (Ft or FPS) are received in error. This must trigger a Severely Errored
			   Seconds Framing (SESF) count for the current second. */
			if (status & (1 << 2)) {
				errors |= XP_ERR_SEFS;
			}
			/* INFO1.3: 16ZD: Sixteen Zero-Detect Event.  Set when a string of at least 16
			   consecutive zeros (regardless of the length of the string) have been received at
			   RPOSI and RNEGI.  Note that these can be counted in the LCVCR error counter. */
			if (status & (1 << 3)) {
				errors |= XP_ERR_LCV;
			}
			/* INFO1.4: 8ZD: Eight Zero-Detect Event.  Set when a string of at least eight
			   consecutive zeros (regardless of the length of the string) have been received at
			   RPOSI and RNEGI.  Note that these can be counted in the LCVCR error counter. */
			if (status & (1 << 4)) {
				errors |= XP_ERR_LCV;
			}
			/* INFO1.5: COFA: Change of Frame Aligment Event.  Set when the last resync resulted
			   in a change of frame alignment or multiframe alignment.  */
			if (status & (1 << 5)) {
				errors |= XP_ERR_FASE;
			}
			/* INFO1.6: TPDV: Transmit Pulse Density Violation Event.  Set when the transmit data 
			   stream does not meet ANSI T1.403 requirements for pulse density.  */
			if (status & (1 << 6)) {
				errors |= XP_ERR_PDV;
			}
			/* INFO1.7: RPDV: Receive Pulse Density Violation Event.  Set when the receive data
			   stream does not meet ANSI T1.403 requirements for pulse density.  */
			if (status & (1 << 7)) {
				errors |= XP_ERR_PDV;
			}
		}
		break;
	case SDL_GTYPE_E1:
		/* E1-only registers */
		/* write-read cycle */
		mask = 0x07;
		xlb[0x12] = mask;
		status = xlb[0x12] & mask;
		if (status) {
			/* INFO3.0: CASRC: CAS Resync Criteria Met. */
			if (status & (1 << 0)) {
			}
			/* INFO3.1: FASRC: FAS Resync Criteria Met. */
			if (status & (1 << 1)) {
			}
			/* INFO3.2: CRCRC: CRC Resync Criteria Met. */
			if (status & (1 << 2)) {
			}
		}
		/* write-read cycle */
		mask = 0xff;
		xlb[0x30] = mask;
		status = xlb[0x30] & mask;
		/* INFO7.0: CRC4SA: CRC4 MF Sync Active. */
		/* G.706 specifies a method to allow automatic interworking between equipment with and
		   without CRC-4 capability.  When basic frame aligment is established the device begins
		   searching for the CRC-4 capability. When basic frame alignment is established the device
		   begins searching for the CRC-4 alignment pattern.  If after 8ms the CRC-4 alignment is not 
		   found, it is assumed that frame frame alignment. After the new frame alignment is
		   established the device starts a new 8ms search period for CRC-4 alignment. If CRC-4
		   alignment is not found, the device starts CRC-4 performance monitoring and setting of the
		   transmitted E-bits according to G.706.  If CRC-4 aligment is not achieved the device
		   continues to return to the basic frame alignment procecedure followed by an 8ms search
		   period for CRC-4. This process continues for 400ms. At the end of this 400ms period, it is 
		   assumed that the far end equipment is non-CRC-4, the search for CRC-4 alignment is
		   terminated and the E-bits trasnmitter toward the far end equipment are set continuously =
		   0.l The DS21455/DS21458 provide a flexible method for implementing this procedure.  Once
		   the device is put into the receive CRC-4 mode, a counter begins to run. The user can
		   access this counter via Information Register 7. */
		/* When automatic E-bit generation is enabled (E1TCRC2.2 = 1), and the transmitter is in
		   CRC-4 mode, the transmitter will automatically set the E-bit according to the following:
		   receive CRC-4 disabled, e-bits = 0; receive CRC-4 enabled but not synchronized, e-bits =
		   0; receive CRC-4 enabled, synchronized, with CRC sub-multiframe codeword error, e-bits =
		   0; receiver syncrhonized in CRC-4 mode with no CRC sub-multiframe codeword errors, e-bits
		   = 1. */

		/* The count of the number of failed 8-ms CRC-4 alignment attempts that have occurred since
		   the last sucessful CRC-4 alignment. This is for G.706 Annex B operation. When the CRC-4
		   alignment has failed for 400-ms (after achieving basic frame alignment), the CRC-4 capable 
		   equipment should assume that the other end is incapable of generating CRC-4 multiframes.A
		   In this case, the transmitter continues transmitting CRC-4 multiframes; however, the
		   E-bits should be set to zero (indicating distant MF alarm). The receiver; however, can be
		   marked as non-CRC-4. */
		if (status & 0x07) {
			if (status & (1 << 0))
				if ((sp->config.ifflags & SDL_IF_AUTOCONFIG)
				    && (sp->config.ifgcrc = SDL_GCRC_CRC4)) {
					count = ((status & 0xf0) >> 2) | ((status & 0x08) >> 3);
					if (count >= 49) {
						/* Basically we have 488 - 408 = 80 ms to determine that
						   there has been a CRC-4 alignment failure for more than 392 
						   milliseconds.  When ECUS is set for 62.5 ms operation, we
						   will detect within 31.25 ms of the event that the CRC-4
						   has failed for 392 milliseconds or more (i.e. before the
						   CRC-4 counter rolls over). */
						/* --1100-0 = 48 = 384 ms */
						/* --1100-0 = 50 = 400 ms */
						/* --1100-1 = 49 = 392 ms */
						/* --1100-1 = 51 = 408 ms */
						/* --1101-0 = 52 = 416 ms */
						/* --1101-0 = 54 = 432 ms */
						/* --1101-1 = 53 = 424 ms */
						/* --1101-1 = 55 = 440 ms */
						/* --1110-0 = 56 = 448 ms */
						/* --1110-0 = 58 = 464 ms */
						/* --1110-1 = 57 = 456 ms */
						/* --1110-1 = 59 = 472 ms */
						/* --1111-0 = 60 = 480 ms */
						/* --1111-0 = 62 = 496 ms */
						/* --1111-1 = 61 = 488 ms */
						/* --1111-1 = 63 = 504 ms */
						/* --0000-0 = 64 = 512 ms */
						/* shut off CRC4 on rx */
						/* E1RCR1.3: RCRC4: 0 = CRC-4 disabled */
						xlb[0x33] &= ~(1 << 3);
						/* Note that this only affects Rx CRC-4. */
					}
				}
			/* INFO7.1: CASSA: CAS MF Sync Active. */
			if (status & (1 << 1)) {
			}
			/* INFO7.2: FASSA: FAS Sync Active. */
			if (status & (1 << 2)) {
			}
		}
		break;
	}

	/* CRC-4 multiframe alignment algorithm: G.706/Annex B: The modified CRC-4 multiframe alignment
	   algorithm is based on the following strategy: If a valid basic frame alignment signal is
	   consistently present but CRC-4 multiframe alignment is not achieved by the end of the total CRC-4
	   multiframe alignment search period, it is assumed that the distant end is a Non-CRC-4 equipment.
	   Under these circumstances, the following consquent actions apply at the equipment having the CRC-4
	   capability: a) provide an indication (not necessarily an alarm) that there is "no incoming CRC-4
	   multiframe alignment signal"; b) inhibit CRC-4 processing on the receive 2048 kbps signal; c)
	   continue to transmit CRC-4 data to the distant end with both "E" bits (see Table 4b/G.704) set to
	   zero.  Note: This allows, as explained in G.706/B.2.5, the identification of failure of CRC-4
	   multiframe alignment generation/detection, but with correct basic framing, when interworking
	   between equipment each having the modified CRC-4 multiframe alignment algorithm.  The algorithm is
	   robust against spurious basic frame alignment and there are no interworking problems with equipment
	   having a manually selectable CRC-4 capability. */
	/* A 400 ms CRC-4 multiframe alignment search period ensures that correct basic and CRC-4 multiframe
	   alignment is possible for up to about 40 spurious simulations of the basic frame aligment sequence 
	   present between tow real basic frame alignment signal locations. The 400 ms timer is triggered on
	   the initial (i.e, primary) recovery of basic frame alignment.  Once the 400 ms timer is triggered, 
	   it is not reset unless the criteria for "loss of (primary) basic frame alignment" occurs (see
	   G.706/4.1.1): The "loss of (primary) basic frame alignemnt" checkinig process runs continuously
	   irrespective of the state of the CRC-4 multiframe aligment process below it. A research for basic
	   frame alignment initiated if CRC-4 multiframe alignment cannot be acheived in 8 ms (as required in 
	   G.706/4.2) should not reset the 400 ms timer or invoke consequent actions associated with loss of
	   primary basic frame alignment, that is, in this particular section of the alignment flow diagram,
	   all searches for basic frame alignment are carried out in parallel with, and hence independent of, 
	   the primary basic frame loss checking process (see Figure B.1/G.706). All subsequent searches for
	   CRC-4 multiframe alignment are associated with each basic framing sequence found during the
	   parallel search.  In order that the algorithm does not introduce a disturbance (of 400 ms maximum
	   duration) to traffic during the search for CRC-4 multiframe alignment, traffic should be allowed
	   through upon, and syncrhonized to, the intially determined primary basic frame alignment sequence. 
	   If a CRC-4 multiframe alignment signal is found before the 400 ms timer elapses, then the basic
	   frame alignment sequence associated with the CRC-4 multiframe alignment signal should be the one
	   chosen, i.e. if necessary, the primary basic frame alignment position should be amended
	   saccordingly.  CRC-4 processing would then determin if this was truly the valid alignment location 
	   (in accordance with 4.3.2/G.706).  However, if a CRC-4 multiframe alignment sequence cannot be
	   found before the 400 ms timer elapses, it should be concluded that a condition of interworking
	   between equipment with and without a CRC-4 capability exists; in this case traffic should be
	   maintained to the initially determined primary basic frame alignment signal location and the
	   consequent actions given in B.2.2/G.706 invoked. If the 2048 kbps path is reconfigured at any
	   time, then it is assumed that the (new) pair of path terminating equipments will need to
	   re-establish the complete framing process, i.e., the algorithm is reset. */

	/* Process Error Counters */

	/* This is a one second timeout.  For T1 this is no really 1 second when performing short
	   timeout-outs, but is 999*8 frames or 0.999 seconds.  But that is ok as we don't accumulate as a
	   result of the interval.  There are some conditions that we want to persist for a full second
	   before we take any actions on them.  One is the JALT.  If the jitter attenuator limit threshold is 
	   being exceeded every short interval for a full second and we have nevery achieved syncrhonization
	   since the last loss of sync or transmitter open circuit, we should think about switching from E1
	   to T1 or vise versa. */
	/* This is a one second timeout. */

	/* Line code violation count register (LCVCR). */

	/* T1 Operation: T1 code violations are defined as bipolar violations (BPVs) or excessive zeros.  If
	   the B8ZS mode is set for the receive side, then B8ZS codewords are not counted.  This counter is
	   always enabled; it is not disbaled during receive loss of synchronization (RLOS=1) conditions. */
	/* ERCNT.0=no exz, T1RCR2.5=AMI, BPVs */
	/* ERCNT.0=exz, T1RCR2.5=AMI, BPVs + 16 consecutive zeros */
	/* ERCNT.0=no exz, T1RCR2.5=B8ZS, BPVs (B8ZS Codewords Not Counted) */
	/* ERCNT.0=exz, T1RCR2.5=B8ZS, BPVs + 8 consecutive zeros */

	/* E1 Operation: Either bipolar violations or code violations can be counted. Bipolar violations are
	   defined as consecutive marks of the same polarity.  In this mode, if the HDB3 mode is set for the
	   receive side, then HDB3 codewords are not counted as BPVs.  If ERCNT.3 is set, then the LVC counts 
	   code violations as defined in IUT O.161.  Code violations are defined as consecutive bipolar
	   violations of the same polarity.  In most applications, the frame should be programmed to count
	   BPVs when receiving AMI code and to count CVs when receiving HDB3 code.  This counter increments
	   at all times and is not disabled by loss of sync conditions. The counter saturates at 65,535 and
	   will not rollover.  The bit error rate on an E1 line would have to be greater than 10E-02 before
	   the VCR would saturate. */
	/* ERCNT.3=0, count BPVs */
	/* ERCNT.3=1, count CVs */

	if ((count = ((uint) (xlb[0x42] & 0xff) << 8) | ((uint) (xlb[0x43] & 0xff) >> 0))) {
		sp->stats[0].LCVs += count;
		errors |= XP_ERR_LCV;
	}

	/* Path code violation count register (PCVCR) */

	/* T1 Operation: The path code violation count register records either Ft, Fs, or CRC6 errors in T1
	   frames. When the receive side of a framer is set to operatin in the T1 ESF framing mode, PCVCR
	   will record errors in the CRC6 codewords. When set to operate in the T1 D4 framing mode, PCVCR
	   will count errors in the Ft framing bit position.  Via the ERCNT.2 bit, a framer can be programmed 
	   to also report errors in the Fs framing bit position.  The PCVCR will be disabled during receive
	   loss of synchronization (RLOS=1) conditions. */
	/* D4, No, Errors in Ft pattern */
	/* D4, Yes, Errors in both Ft and Fs patterns. */
	/* ESF, don't care, Errors in the CRC6 codewords. */

	/* E1 Operation: The PCVCR records CRC-4 errors.  Since the maximum CRC4 count in a one-second period 
	   is 1000, this counter cannot saturate.  The counter is disabled during loss of sync at either the
	   FAS or CRC-4 level; it will continue to count if loss of multiframe sync occurs at the CAS level. */

	if ((count = ((uint) (xlb[0x44] & 0xff) << 8) | ((uint) (xlb[0x45] & 0xff) >> 0))) {
		sp->stats[0].PCVs += count;
		errors |= XP_ERR_PCV;
	}

	/* Frames out of sync count register (FOSCR) */

	/* T1 Operation: The FOSCR is used to count the number of multiframes that the receive synchronizer
	   is out of sync.  This number is useful in ESF applications needing to measure the parameters loss
	   of frame count (LOFC) and ESF error events as described in AT&T publication TR54016.  When the
	   FOSCR is operated in this mode, it is not disabled during receive loss of synchronization (RLOS=1) 
	   conditions.  The FOSCR has alternate operating mode whereby it will count either errors in the Ft
	   framing pattern (in the D4 mode) or errors in the FPS framing pattern (in the ESF mode).  WHen the 
	   FOSCR is operated in this mode, it is disabled during receive loss of synchronization (RLOS=1)
	   conditions. */
	/* T1RCR1.3=D4, ERCNT.1=MOS, number of multiframes out of sync */
	/* T1RCR1.3=D4, ERCNT.1=F-bit, errors in the Ft pattern */
	/* T1RCR1.3=ESF, ERCNT.1=MOS, number of multiframes out of sync */
	/* T1RCR1.3=ESF, ERCNT.1=F-bit, errors in the FPS pattern */

	/* E1 Operation: The FOSCR counts word errors in the frame alignment signal in time slot 0. This
	   counter is disabled when RLOS=1.  FAS errors will not be counted when the framer is searching for
	   FAS alignment and/or synchronization at either the CAS or CRC-4 multiframe level.  Since the
	   maximum FAS word error count in a one-second period is 4000, this counter cannot saturate. */
	if ((count = ((uint) (xlb[0x46] & 0xff) << 8) | ((uint) (xlb[0x47] & 0xff) >> 0))) {
		sp->stats[0].FASEs += count;
		errors |= XP_ERR_FASE;
	}

	/* E-Bit Counter Register (EBCR) */

	/* This counter is only available in E1 mode.  The 16-bit counter records far end block errors
	   (FEBE), as reported in the first bits of frame 13 and 15 on E1 lines running with CRC-4
	   multiframe.  These count registers will increment once each time the received E-bit is set to
	   zero.  Since the maximum E-bit count in a one second period is 1000, this counter will not
	   saturate.  The counter is disabled during loss of sync at either the FAS or CRC-4 level; it will
	   continue to count if loss of multiframe sync occurs at the CAS level. */

	if (sp->config.ifgtype == SDL_GTYPE_E1) {
		if ((count = ((uint) (xlb[0x48] & 0xff) << 8) | ((uint) (xlb[0x49] & 0xff) >> 0))) {
			sp->stats[0].FEBEs += count;
			errors |= XP_ERR_FEBE;
		}
	}

	xp_x1_process_state(cd, sp, xlb, flags, errors);

	{
		int interval;		/* interval in frames */

		/* ERCNT.5: ECUS: 1 = 333/500 frames, 0 = 8000 frames */
		if (xlb[0x41] & 0x20) {
			if (sp->config.ifgtype == SDL_GTYPE_E1)
				interval = 500;	/* frames, 62.5ms */
			else
				interval = 333;	/* frames, 41.625ms */
		} else
			interval = 8000;	/* frames, 1.000 sec */

		if ((sp->interval += interval) >= 8000) {
			sp->interval -= 8000;
			/* this is a 1 second timeout */
			xp_x1_process_stats(sp, errors);
			sp->status.errors = 0;
		} else
			sp->status.errors = errors;
	}
	return;
}

/** xp_x1_process_state: - process state transitions for the device.
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span iobase
  * @flags: state flags (read only)
  * @errors: error flags (read only)
  *
  * Process state transitions once a timeout.  This is to ensure that the interface state does not
  * change due to transient conditions of less than 40 ms.  Also, any events or errors here have
  * accumulated for the entire timeout period (more than 40 ms).
  *
  * Processing DS2155/455/458 state transitions is eased by the wide range of receive level
  * determination as well as the ability to detect when the transmitters are open-circuit.  This
  * makes it easier to determine whether the span is in a monitor configuration and whether linear
  * gain should be applied to the receievers.
  */
static noinline fastcall __hot void
xp_x1_process_state(struct cd *cd, struct sp *sp, volatile uint8_t *xlb, const register uint flags, const register
		    uint errors)
{
	register uint ifflags = sp->config.ifflags;

	/* integrate receiver state */
	if (flags & (XP_STATE_LRCL | XP_STATE_FRCL)) {
		ifflags &= ~SDL_IF_RX_UP;
		ifflags &= ~SDL_IF_RX_RUNNING;
	} else {
		ifflags |= SDL_IF_RX_UP;
		if (flags & (XP_STATE_ILUT | XP_STATE_RLOS | XP_STATE_RUA1 | XP_STATE_LORC)) {
			ifflags &= ~SDL_IF_RX_RUNNING;
		} else {
			ifflags |= SDL_IF_RX_RUNNING;
		}
	}

	/* integrate transmitter state */
	if (flags & (XP_STATE_TOCD | XP_STATE_TSCD)) {
		ifflags &= ~SDL_IF_TX_UP;
		ifflags &= ~SDL_IF_TX_RUNNING;
	} else {
		ifflags |= SDL_IF_TX_UP;
		if (flags & (XP_STATE_LOLITC | XP_STATE_LOTC)) {
			ifflags &= ~SDL_IF_TX_RUNNING;
		} else {
			ifflags |= SDL_IF_TX_RUNNING;
		}
	}

	/* integrate interface state */
	if (flags & (SDL_IF_RX_UP | SDL_IF_TX_UP)) {
		ifflags |= SDL_IF_UP;
	} else {
		ifflags &= ~SDL_IF_UP;
	}

	/* detect changes in transmitter state */
	if ((ifflags ^ sp->config.ifflags) & (SDL_IF_TX_UP | SDL_IF_TX_RUNNING)) {
	}

	/* detect changes in receiver state */
	if ((ifflags ^ sp->config.ifflags) & (SDL_IF_RX_UP | SDL_IF_RX_RUNNING)) {
	}

	/* detect changes in interface state */
	if ((ifflags ^ sp->config.
	     ifflags) & (SDL_IF_UP | SDL_IF_TX_UP | SDL_IF_RX_UP | SDL_IF_TX_RUNNING | SDL_IF_RX_RUNNING)) {
	}

	/* perform autoconfiguration when necessary */
	if (ifflags & SDL_IF_AUTOCONFIG) {
		if (likely(sp->status.state == 40)) {
			if (unlikely((flags & (XP_STATE_LRCL)) != 0))
				/* lost carrier, start full autoconfig */
				sp->status.state = 0;
			else if (unlikely((flags & (XP_STATE_FRCL)) != 0))
				/* lost signal, start partial autoconfig */
				sp->status.state = 20;
		}
		if (unlikely(sp->status.state != 40))
			/* lost configuration hold, process autoconfig */
			xp_x1_process_auto(cd, sp, xlb, flags, errors, 1);
	}
}

/** xp_x1_process_auto: - process autoconfiguration state machine
  * @cd: card structure pointer
  * @sp: span structure pointer
  * @xlb: span iobase
  * @flags: state flags
  * @errors: error events
  * @timeout: true when timeout
  *
  * Note that we do not even execute this function unless SDL_IF_AUTOCONFIG is set in the interface
  * flags.  The autoconfiguration states are a little different from the E1 and T1 chips because the
  * E1/T1 chip can do jitter attenuator trip limit detection (to detect the line rate) and
  * transmitter open- and short-circuit detection.  Also the DS2155/455/458 chip can perform linear
  * gain for monitoring applications of 20dB, 26dB and 30dB.  20dB corresponds to a single 432-470
  * Ohm isolation resistors on a single high-impedance monitoring tap.  26dB corresponds to double
  * 432-470 Ohm isolation resistors (tap of a tap).  30dB coresponds to triple 432-470 Ohm isolation
  * resistors (tap of a tap of a tap).  The E1 and T1 only chips can only handle a single tap.
  */
static noinline fastcall __unlikely void
xp_x1_process_auto(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint flags, const
		   register uint errors, const uint timeout)
{
	register uint state = sp->status.state;
	const uint interval =
	    timeout ? ((xlb[0x41] & 0x20) ? ((sp->config.ifgtype == SDL_GTYPE_E1) ? 500 : 333) : 8000) : 0;

	for (;;) {
		do {
			if (flags & XP_STATE_LIRST) {
				/* resetting line interface */
				sp->status.count = 0;
				sp->status.config = 0;
				state = 0;
				break;
			}
			if ((flags & XP_STATE_LRCL) && (11 <= state)) {
				/* lost carrier */
				sp->status.count = 0;
				state = 0;
				break;
			}
			if ((flags & XP_STATE_FRCL) && (12 <= state)) {
				/* lost signal */
				sp->status.count = 0;
				state = 11;
				break;
			}
			if ((flags & XP_STATE_RUA1) && (13 <= state)) {
				/* lost service */
				sp->status.count = 0;
				state = 12;
				break;
			}
			if ((flags & XP_STATE_RLOS) && (15 <= state && state < 18)) {
				/* lost sync */
				sp->status.count = 0;
				state = 14;
				break;
			}
		} while (0);
		switch (state) {
		default:
			state = 0;
			/* fall through */
		case 0:
			/* LIRST subsidance.  Wait 125 ms or more when the LIRST condiiton remains, then
			   clear the condition and move on to line detection. */
			if (flags & (XP_STATE_LIRST)) {
				if ((sp->status.count += interval) < 1000) {
					/* wait for up to 125 ms */
					break;
				}
				/* remove LIRST flag */
				sp->status.flags &= ~(XP_STATE_LIRST);
				flags &= ~(XP_STATE_LIRST);
				/* reset elastic stores */
				/* ESCR.6=0 TESR 0->1 reset. */
				/* ESCR.2=0 RESR 0->1 reset. */
				xlb[0x4f] &= ~((1 << 6) | (1 << 2));
				xlb[0x4f] |= (1 << 6) | (1 << 2);
				xlb[0x4f] &= ~((1 << 6) | (1 << 2));
				/* realign elastic stores */
				/* ESCR.7=0 TESALIGN 0->1 align. */
				/* ESCR.3=0 RESALGN 0->1 align. */
				xlb[0x4f] &= ~((1 << 7) | (1 << 3));
				xlb[0x4f] |= (1 << 7) | (1 << 3);
				xlb[0x4f] &= ~((1 << 7) | (1 << 3));
			}
			/* remove linear gain */
			/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
			xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x0 << 3);
			/* start transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] |= (1 << 0);
			/* send unframed all ones */
			switch (sp->config.ifgtype) {
			case SDL_GTYPE_E1:
				/* E1TCR1.5: TUA1: Transmit Unframed All Ones. 0, normal; 1, tua1. */
				xlb[0x35] |= (1 << 5);
				break;
			case SDL_GTYPE_T1:
			case SDL_GTYPE_J1:
				/* T1TCR1.1: TBL: Transmit blue alarm (0, data; 1, unframed all ones) */
				xlb[0x05] |= (1 << 1);
				break;
			}
			sp->status.count = 0;
			sp->config.ifflags &= ~(SDL_IF_UP | SDL_IF_RX_UP | SDL_IF_TX_UP | SDL_IF_RX_MON);
			state = 1;
			/* fall through */
		case 1:
			/* Line carrier detect, 0dB gain. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying linear gain */
					/* apply 20dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x1 << 3);
					sp->status.count = 0;
					state = 2;
					continue;
				}
				/* wait for 125 ms */
				break;
			}
			/* turn on transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] |= (1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 11;
			continue;
		case 2:
			/* Line carrier detect, monitoring, 20dB gain.  No line carrier was detected with 0dB 
			   linear gain, try applying 20dB gain.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying more linear gain */
					/* apply 26dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x2 << 3);
					sp->status.count = 0;
					state = 3;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 7;
			continue;
		case 3:
			/* Line carrier detect, monitoring, 26dB gain.  No line carrier was detected wtih 0dB 
			   and 20dB linea gain, try applying 26dB gain. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try applying more linear gain */
					/* apply 30dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x3 << 3);
					sp->status.count = 0;
					state = 4;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* apply 20dB linear gain */
			sp->status.count = 0;
			state = 6;
			continue;
		case 4:
			/* Line carrier detect, monitoring, 30dB gain.  No line carrier was detected with
			   0dB, 20dB and 26dB linear gain, try applying 30dB gain. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* try removing gain again */
					/* apply 0dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x0 << 3);
					sp->status.count = 0;
					state = 1;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* apply 26dB linear gain */
			sp->status.count = 0;
			state = 5;
			/* fall through */
		case 5:
			/* Line carrier detect, monitoring, 26dB gain.  30dB gain was applied previously with 
			   success, drop back to 26dB gain to test that things were not just connected at a
			   bad time. */
			/* apply 20dB linear gain */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 30dB gain */
					/* apply 30dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x3 << 3);
					sp->status.count = 0;
					state = 8;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* apply 20dB linear gain */
			sp->status.count = 0;
			state = 6;
			/* fall through */
		case 6:
			/* Line carrier detect, monitoring, 20dB gain. 26dB gain was applied previously with
			   success, drop back to 20dB gain to test that things were not just connected at a
			   bad time. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 26dB gain */
					/* apply 26dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x2 << 3);
					sp->status.count = 0;
					state = 9;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* apply 0dB linear gain */
			sp->status.count = 0;
			state = 7;
			/* fall through */
		case 7:
			/* Line carrier detect, monitoring, 0dB gain.  20dB gain was applied previously with
			   success, drop back to 0dB gain to test that things were not just connected at a
			   bad time. */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* put back to 20dB gain */
					/* apply 20dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x1 << 3);
					sp->status.count = 0;
					state = 10;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* turn on transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] |= (1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 11;
			break;
		case 8:
			/* Line carrier detected, monitoring, 30dB gain.  30dB worked, 26dB didn't, come back 
			   to 30 dB and lock in if successful.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 30dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x0 << 3);
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* turn off transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] &= ~(1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 11;
			continue;
		case 9:
			/* Line carrier detected, monitoring, 26dB gain.  26dB worked, 20dB didn't, come back 
			   to 26dB and lock in if successful.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 26dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x0 << 3);
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* turn off transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] &= ~(1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 11;
			continue;
		case 10:
			/* Line carrier detected, monitoring, 20dB gain.  20dB worked, 0dB didn't, come back
			   to 20dB and lock in if successful.  */
			if (flags & (XP_STATE_LRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* 20dB worked before but doesn't now, start again */
					/* apply 0dB linear gain */
					/* LIC3.4-3: MM1-MM0: 00, no gain; 01, 20dB; 10, 26dB; 11, 32dB. */
					xlb[0x7a] = (xlb[0x7a] & ~((1 << 4) | (1 << 3))) | (0x0 << 3);
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			/* turn off transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] &= ~(1 << 0);
			sp->config.ifflags |= SDL_IF_RX_MON;
			sp->status.count = 0;
			state = 11;
			/* fall through */
		case 11:
			/* Framer signal detect.  Line carrier detected, 0dB, 20dB, 26dB or 30dB linear gain
			   applied.  When other than 0dB linear gain is applied, SDL_IF_RX_MON flag is set.  */
			if (flags & (XP_STATE_FRCL)) {
				if ((sp->status.count += interval) >= 1000) {
					/* no framer signal, start again */
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 12;
			/* fall through */
		case 12:
			/* Framing detect, sending unframed all ones.  If we are in a loop-around mode we
			   will wait here for manual (specific) configuration because we will be both sending 
			   and receiving unframed all ones.  However, only wait for 10 seconds and then
			   restart in case there is an error in the state machine that locks us here. */
			if (flags & (XP_STATE_RUA1)) {
				if ((sp->status.count += interval) >= 10 * 8000) {
					/* no service for 10 seconds, start again */
					sp->status.count = 0;
					state = 0;
					continue;
				}
				/* wait for up to 10 seconds */
				break;
			}
			sp->status.count = 0;
			state = 13;
			/* fall through */
		case 13:
			/* Frequency detect.  We have a carrer and a signal.  We should be marshalling bits
			   through the jitter attenuator so check whether the trip limit has been exceeded.
			   This is because we might have the wrong E1/T1 setting and the attenuator will trip 
			   immediately in that case.  It might trip anyway if we have the wrong
			   synchronization.  We really only need to check the jitter attenuator if we cannot
			   acheive frame syncrhonization.  Note that once the JALT has tripped we need to do
			   a LIRST to reset the JA position. */
			if (flags & (XP_STATE_JALT)) {
				if (xp_x1_change_gtype(sp, xlb)) {
					sp->status.flags |= XP_STATE_LIRST;
					sp->status.config = 0;
					sp->status.count = 0;
					state = 0;
					continue;
				}
			}
			sp->status.count = 0;
			state = 14;
			/* fall through */
		case 14:
			/* Frame detect, not receiving unframed all ones. */
			if (flags & (XP_STATE_RLOS)) {
				if ((sp->status.count += interval) >= 3200) {
					/* no sync after 400 ms */
					/* reconfigure and try again */
					if (xp_x1_change_config(sp, xlb)) {
						/* exhausted possibilities, start again */
						sp->status.count = 0;
						state = 0;
						continue;
					}
					sp->status.count = 0;
					/* wait to test possibility */
					break;
				}
				/* wait for up to 400 ms */
				break;
			}
			sp->status.count = 0;
			state = 15;
			/* fall through */
		case 15:
			/* Transmitter detection, transmitting unframed all ones.  We are synchronized on the 
			   receiver.  Check whether the transmitter is open or short circuited.  When
			   transmitters are not externall connected we are in monitoring mode only. */
			/* Note that the transmitter might not be able to detect open- or short-circuit when
			   the transmitter is tri-state.  However, we only tri-state the transmitter when we
			   already know that we are in monitoring mode (linear gain has been applied), so
			   this should not be a problem. */
			if (flags & (XP_STATE_TOCD | XP_STATE_TSCD)) {
				if ((sp->status.count += interval) >= 1000) {
					/* open or short circuit for 125 ms */
					/* No transmitter connection after 125 milliseconds.  When no linear
					   gain is applied, this is a 0dB active monitoring tap.  When linear 
					   gain is applied, this is a high-impeadance monitoring tap. */
					/* turn off transmitter */
					/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
					xlb[0x78] &= ~(1 << 0);
					sp->config.ifflags |= SDL_IF_RX_MON;
					sp->config.ifflags &= ~(SDL_IF_TX_UP);
					sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
					sp->status.count = 0;
					state = 18;
					continue;
				}
				/* wait up to 125 ms */
				break;
			}
			sp->status.count = 0;
			state = 16;
			/* fall through */
		case 16:
			/* Transmitter detection, externally connected, transmitting unframed all ones.  The
			   transmitters are not open- or short-circuited, but they might still be
			   disconnected from the interface (e.g. terminated by an active monitoring tap).  We 
			   are transmitting unframed all ones, so when there is no remote alarm after 125 ms
			   the transmitters must be disconnected. */
			if (!(flags & (XP_STATE_RYEL | XP_STATE_RRA))) {
				if ((sp->status.count += interval) >= 1000) {
					/* No alarm after 125 milliseconds, transmitters must be
					   disconnected.  When no linear gain is applied, but transmitters
					   are disconnected: this is a 0dB active monitoring tap.  When
					   linear gain applied, already monitoring so this is an expected
					   result.  */
					/* turn off transmitter */
					/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
					xlb[0x78] &= ~(1 << 0);
					sp->config.ifflags |= SDL_IF_RX_MON;
					sp->config.ifflags &= ~(SDL_IF_TX_UP);
					sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
					sp->status.count = 0;
					state = 18;
					continue;
				}
				/* wait up to 125 ms */
				break;
			}
			/* transmit normal frames */
			switch (sp->config.ifgtype) {
			case SDL_GTYPE_E1:
				/* E1TCR1.5: TUA1: Transmit Unframed All Ones. 0, normal; 1, tua1. */
				xlb[0x35] &= ~(1 << 5);
				break;
			case SDL_GTYPE_T1:
			case SDL_GTYPE_J1:
				/* T1TCR1.1: TBL: Transmit blue alarm (0, data; 1, unframed all ones) */
				xlb[0x05] &= ~(1 << 1);
				break;
			}
			sp->status.count = 0;
			state = 17;
			/* fall through */
		case 17:
			/* Transmitter detection, sending normal frames.  We previously had a remote alarm
			   condition when sending unframed all ones.  We start sending normal frames and
			   check that the alarm clears within 10 seconds. */
			if (flags & (XP_STATE_RYEL | XP_STATE_RRA)) {
				if ((sp->status.count += interval) < 10 * 8000) {
					/* wait up to 10 seconds */
					break;
				}
				/* Alarms did not clear after 10 seconds, but this might just be a sync
				   problem. */
			}
			/* turn on transmitter */
			/* LIC1.0=? TBD Transmit Power-Down (0, power-down; 1, normal) */
			xlb[0x78] |= (1 << 0);
			sp->config.ifflags &= ~SDL_IF_RX_MON;
			sp->config.ifflags |= (SDL_IF_TX_UP);
			sp->config.ifflags |= (SDL_IF_RX_UP | SDL_IF_UP);
			sp->status.count = 0;
			state = 18;
			/* fall through */
		case 18:
			/* Stable state, normal mode, transmitters externally connected, sending normal
			   frames.  This is an active connection, not a monitoring tap. Flags have been set
			   accordingly.  Stay in this state unless we lose carrier, signal, start receiving
			   unframed all ones, or lose sync for more than 400 ms. */
			/* Stable state, monitoring mode, transmitters externally disconnected, sending
			   unframed all ones.  This is a monitoring tap (active or passive).  Flags have been 
			   set accordingly.  Stay in this state unless we lose carrier, signal, start
			   receiving unframed all ones, or lose sync for more than 400 ms. */
			if (flags & (XP_STATE_RLOS)) {
				/* lost sync */
				if ((sp->status.count += interval) >= 3200) {
					/* send unframed all ones */
					switch (sp->config.ifgtype) {
					case SDL_GTYPE_E1:
						/* E1TCR1.5: TUA1: Transmit Unframed All Ones. 0, normal; 1,
						   tua1. */
						xlb[0x35] |= (1 << 5);
						break;
					case SDL_GTYPE_T1:
					case SDL_GTYPE_J1:
						/* T1TCR1.1: TBL: Transmit blue alarm (0, data; 1, unframed
						   all ones) */
						xlb[0x05] |= (1 << 1);
						break;
					}
					sp->status.count = 0;
					state = 14;
					continue;
				}
				/* wait for up to 400 ms */
				break;
			}
			sp->status.count = 0;
			/* stay in this state */
			break;
		}
		break;
	}
	/* set the new state variable */
	sp->status.state = state;
}

/** xp_x1_change_config: - try changing span configuration for sync
  * @sp: span structure pointer
  * @xlb: span iobase
  *
  * Diagnose the problem and attempt to change the configuration accordingly.  This is a T1/J1/E1
  * line so likely difficulties are ESF instead of D4 or CCS instead of CAS; CRC6 instead of CRC6J
  * or CRC4 instead of non-CRC4; B8ZS instead of AMI or HDB3 instead of AMI.  We should simply walk
  * through the possible configurations until one that works is found or all are exhausted.
  */
noinline __unlikely int
xp_x1_change_config(struct sp *sp, register volatile uint8_t *xlb)
{
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		switch (sp->status.config) {
		case 0:	/* E1, CCS, HDB3, CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CCS) {
				sp->config.ifframing = SDL_FRAMING_CCS;
				xlb[0x40] |= (1 << 1);	/* SIGCR.1: TCCS: 1 = Tx CCS. */
				xlb[0x40] |= (1 << 2);	/* SIGCR.2: RCCS: 1 = Rx CCS. */
				xlb[0x33] |= (1 << 6);	/* E1RCR1.6: RSIGM: 1 = Rx CCS */
			}
			if (sp->config.ifcoding != SDL_CODING_HDB3) {
				sp->config.ifcoding = SDL_CODING_HDB3;
				xlb[0x33] |= (1 << 5);	/* E1RCR1.5: RHDB3: 1 = Rx HDB3. */
				xlb[0x35] |= (1 << 2);	/* E1TCR1.2: THDB3: 1 = Tx HDB3. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
				sp->config.ifgcrc = SDL_GCRC_CRC5;
				xlb[0x33] &= ~(1 << 3);	/* E1RCR1.3: RCRC4: 0, Rx non-CRC-4. */
				xlb[0x35] &= ~(1 << 0);	/* E1TCR1.0: TCRC4: 0, Tx non-CRC-4.  */
			}
			sp->status.config = 1;
			break;
		case 1:	/* E1, CCS, HDB3, non-CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CCS) {
				sp->config.ifframing = SDL_FRAMING_CCS;
				xlb[0x40] |= (1 << 1);	/* SIGCR.1: TCCS: 1 = Tx CCS. */
				xlb[0x40] |= (1 << 2);	/* SIGCR.2: RCCS: 1 = Rx CCS. */
				xlb[0x33] |= (1 << 6);	/* E1RCR1.6: RSIGM: 1 = Rx CCS */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x33] &= ~(1 << 5);	/* E1RCR1.5: RHDB3: 0 = Rx AMI. */
				xlb[0x35] &= ~(1 << 2);	/* E1TCR1.2: THDB3: 0 = Tx AMI. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
				sp->config.ifgcrc = SDL_GCRC_CRC4;
				xlb[0x33] |= (1 << 3);	/* E1RCR1.3: RCRC4: 1, Rx CRC-4. */
				xlb[0x35] |= (1 << 0);	/* E1TCR1.0: TCRC4: 1, Tx CRC-4.  */
			}
			sp->status.config = 2;
			break;
		case 2:	/* E1, CCS, AMI, CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CCS) {
				sp->config.ifframing = SDL_FRAMING_CCS;
				xlb[0x40] |= (1 << 1);	/* SIGCR.1: TCCS: 1 = Tx CCS. */
				xlb[0x40] |= (1 << 2);	/* SIGCR.2: RCCS: 1 = Rx CCS. */
				xlb[0x33] |= (1 << 6);	/* E1RCR1.6: RSIGM: 1 = Rx CCS */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x33] &= ~(1 << 5);	/* E1RCR1.5: RHDB3: 0 = Rx AMI. */
				xlb[0x35] &= ~(1 << 2);	/* E1TCR1.2: THDB3: 0 = Tx AMI. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
				sp->config.ifgcrc = SDL_GCRC_CRC5;
				xlb[0x33] &= ~(1 << 3);	/* E1RCR1.3: RCRC4: 0, Rx non-CRC-4. */
				xlb[0x35] &= ~(1 << 0);	/* E1TCR1.0: TCRC4: 0, Tx non-CRC-4.  */
			}
			sp->status.config = 3;
			break;
		case 3:	/* E1, CCS, AMI, non-CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CAS) {
				sp->config.ifframing = SDL_FRAMING_CAS;
				xlb[0x40] &= ~(1 << 1);	/* SIGCR.1: TCCS: 0 = Tx CAS. */
				xlb[0x40] &= ~(1 << 2);	/* SIGCR.2: RCCS: 0 = Rx CAS. */
				xlb[0x33] &= ~(1 << 6);	/* E1RCR1.6: RSIGM: 0 = Rx CAS. */
			}
			if (sp->config.ifcoding != SDL_CODING_HDB3) {
				sp->config.ifcoding = SDL_CODING_HDB3;
				xlb[0x33] |= (1 << 5);	/* E1RCR1.5: RHDB3: 1 = Rx HDB3. */
				xlb[0x35] |= (1 << 2);	/* E1TCR1.2: THDB3: 1 = Tx HDB3. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
				sp->config.ifgcrc = SDL_GCRC_CRC4;
				xlb[0x33] |= (1 << 3);	/* E1RCR1.3: RCRC4: 1, Rx CRC-4. */
				xlb[0x35] |= (1 << 0);	/* E1TCR1.0: TCRC4: 1, Tx CRC-4.  */
			}
			sp->status.config = 4;
			break;
		case 4:	/* E1, CAS, HDB3, CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CAS) {
				sp->config.ifframing = SDL_FRAMING_CAS;
				xlb[0x40] &= ~(1 << 1);	/* SIGCR.1: TCCS: 0 = Tx CAS. */
				xlb[0x40] &= ~(1 << 2);	/* SIGCR.2: RCCS: 0 = Rx CAS. */
				xlb[0x33] &= ~(1 << 6);	/* E1RCR1.6: RSIGM: 0 = Rx CAS. */
			}
			if (sp->config.ifcoding != SDL_CODING_HDB3) {
				sp->config.ifcoding = SDL_CODING_HDB3;
				xlb[0x33] |= (1 << 5);	/* E1RCR1.5: RHDB3: 1 = Rx HDB3. */
				xlb[0x35] |= (1 << 2);	/* E1TCR1.2: THDB3: 1 = Tx HDB3. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
				sp->config.ifgcrc = SDL_GCRC_CRC5;
				xlb[0x33] &= ~(1 << 3);	/* E1RCR1.3: RCRC4: 0, Rx non-CRC-4. */
				xlb[0x35] &= ~(1 << 0);	/* E1TCR1.0: TCRC4: 0, Tx non-CRC-4.  */
			}
			sp->status.config = 5;
			break;
		case 5:	/* E1, CAS, HDB3, non-CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CAS) {
				sp->config.ifframing = SDL_FRAMING_CAS;
				xlb[0x40] &= ~(1 << 1);	/* SIGCR.1: TCCS: 0 = Tx CAS. */
				xlb[0x40] &= ~(1 << 2);	/* SIGCR.2: RCCS: 0 = Rx CAS. */
				xlb[0x33] &= ~(1 << 6);	/* E1RCR1.6: RSIGM: 0 = Rx CAS. */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x33] &= ~(1 << 5);	/* E1RCR1.5: RHDB3: 0 = Rx AMI. */
				xlb[0x35] &= ~(1 << 2);	/* E1TCR1.2: THDB3: 0 = Tx AMI. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
				sp->config.ifgcrc = SDL_GCRC_CRC4;
				xlb[0x33] |= (1 << 3);	/* E1RCR1.3: RCRC4: 1, Rx CRC-4. */
				xlb[0x35] |= (1 << 0);	/* E1TCR1.0: TCRC4: 1, Tx CRC-4.  */
			}
			sp->status.config = 6;
			break;
		case 6:	/* E1, CAS, AMI, CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CAS) {
				sp->config.ifframing = SDL_FRAMING_CAS;
				xlb[0x40] &= ~(1 << 1);	/* SIGCR.1: TCCS: 0 = Tx CAS. */
				xlb[0x40] &= ~(1 << 2);	/* SIGCR.2: RCCS: 0 = Rx CAS. */
				xlb[0x33] &= ~(1 << 6);	/* E1RCR1.6: RSIGM: 0 = Rx CAS. */
			}
			if (sp->config.ifcoding != SDL_CODING_AMI) {
				sp->config.ifcoding = SDL_CODING_AMI;
				xlb[0x33] &= ~(1 << 5);	/* E1RCR1.5: RHDB3: 0 = Rx AMI. */
				xlb[0x35] &= ~(1 << 2);	/* E1TCR1.2: THDB3: 0 = Tx AMI. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC5) {
				sp->config.ifgcrc = SDL_GCRC_CRC5;
				xlb[0x33] &= ~(1 << 3);	/* E1RCR1.3: RCRC4: 0, Rx non-CRC-4. */
				xlb[0x35] &= ~(1 << 0);	/* E1TCR1.0: TCRC4: 0, Tx non-CRC-4.  */
			}
			sp->status.config = 7;
			break;
		case 7:	/* E1, CAS, AMI, non-CRC4 */
			sp->config.ifgtype = SDL_GTYPE_E1;
			if (sp->config.ifframing != SDL_FRAMING_CCS) {
				sp->config.ifframing = SDL_FRAMING_CCS;
				xlb[0x40] |= (1 << 1);	/* SIGCR.1: TCCS: 1 = Tx CCS. */
				xlb[0x40] |= (1 << 2);	/* SIGCR.2: RCCS: 1 = Rx CCS. */
				xlb[0x33] |= (1 << 6);	/* E1RCR1.6: RSIGM: 1 = Rx CAS. */
			}
			if (sp->config.ifcoding != SDL_CODING_HDB3) {
				sp->config.ifcoding = SDL_CODING_HDB3;
				xlb[0x33] |= (1 << 5);	/* E1RCR1.5: RHDB3: 1 = Rx HDB3. */
				xlb[0x35] |= (1 << 2);	/* E1TCR1.2: THDB3: 1 = Tx HDB3. */
			}
			if (sp->config.ifgcrc != SDL_GCRC_CRC4) {
				sp->config.ifgcrc = SDL_GCRC_CRC4;
				xlb[0x33] |= (1 << 3);	/* E1RCR1.3: RCRC4: 1, Rx CRC-4. */
				xlb[0x35] |= (1 << 0);	/* E1TCR1.0: TCRC4: 1, Tx CRC-4.  */
			}
			sp->status.config = 0;
			return (1);
		}
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		switch (sp->status.config) {
		case 0:	/* T1, ESF, B8ZS, CRC6 */
			if (!ansi || japan) {
				sp->config.ifgtype = SDL_GTYPE_J1;
				if (sp->config.ifframing != SDL_FRAMING_ESF) {
					sp->config.ifframing = SDL_FRAMING_ESF;
					xlb[0x04] |= (1 << 6);	/* T1RCR2.6: RFM: 1 = Rx ESF. */
					xlb[0x07] |= (1 << 2);	/* T1CCR1.2: TFM: 1 = Tx ESF. */
				}
				if (sp->config.ifcoding != SDL_CODING_B8ZS) {
					sp->config.ifcoding = SDL_CODING_B8ZS;
					xlb[0x04] |= (1 << 5);	/* T1RCR2.5: RB8ZS: 1 = Rx B8ZS. */
					xlb[0x06] |= (1 << 7);	/* T1TCR2.7: TB8ZS: 1 = Tx B8ZS. */
					xlb[0x41] |= (1 << 3);	/* ERCNT.3: VCRFS: 1 = count CVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
					sp->config.ifgcrc = SDL_GCRC_CRC6J;
					xlb[0x04] |= (1 << 1);	/* T1RCR2.1: RJC: 1 = Rx JT-G704. */
					xlb[0x05] |= (1 << 7);	/* T1TCR1.7: TJC: 1 = Tx JT-G704. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = bit 2. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = bit 2. */
				}
				sp->status.config = 1;
				break;
			}
			/* fall through */
		case 1:	/* J1, ESF, B8ZS, CRC6J */
			if (ansi || !japan) {
				sp->config.ifgtype = SDL_GTYPE_T1;
				if (sp->config.ifframing != SDL_FRAMING_ESF) {
					sp->config.ifframing = SDL_FRAMING_ESF;
					xlb[0x04] |= (1 << 6);	/* T1RCR2.6: RFM: 1 = Rx ESF. */
					xlb[0x07] |= (1 << 2);	/* T1CCR1.2: TFM: 1 = Tx ESF. */
				}
				if (sp->config.ifcoding != SDL_CODING_AMI) {
					sp->config.ifcoding = SDL_CODING_AMI;
					xlb[0x04] &= ~(1 << 5);	/* T1RCR2.5: RB8ZS: 0 = Rx AMI. */
					xlb[0x06] &= ~(1 << 7);	/* T1TCR2.7: TB8ZS: 0 = Tx AMI. */
					xlb[0x41] &= ~(1 << 3);	/* ERCNT.3: VCRFS: 0 = count BPVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
					sp->config.ifgcrc = SDL_GCRC_CRC6;
					xlb[0x04] &= ~(1 << 1);	/* T1RCR2.1: RJC: 0 = Rx ANSI CRC6. */
					xlb[0x05] &= ~(1 << 7);	/* T1TCR1.7: TJC: 0 = Tx ANSI CRC6. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = Rx ANSI. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = Tx ANSI. */
				}
				sp->status.config = 2;
				break;
			}
			/* fall through */
		case 2:	/* T1, ESF, AMI, CRC6 */
			if (!ansi || japan) {
				sp->config.ifgtype = SDL_GTYPE_J1;
				if (sp->config.ifframing != SDL_FRAMING_ESF) {
					sp->config.ifframing = SDL_FRAMING_ESF;
					xlb[0x04] |= (1 << 6);	/* T1RCR2.6: RFM: 1 = Rx ESF. */
					xlb[0x07] |= (1 << 2);	/* T1CCR1.2: TFM: 1 = Tx ESF. */
				}
				if (sp->config.ifcoding != SDL_CODING_AMI) {
					sp->config.ifcoding = SDL_CODING_AMI;
					xlb[0x04] &= ~(1 << 5);	/* T1RCR2.5: RB8ZS: 0 = Rx AMI. */
					xlb[0x06] &= ~(1 << 7);	/* T1TCR2.7: TB8ZS: 0 = Tx AMI. */
					xlb[0x41] &= ~(1 << 3);	/* ERCNT.3: VCRFS: 0 = count BPVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
					sp->config.ifgcrc = SDL_GCRC_CRC6J;
					xlb[0x04] |= (1 << 1);	/* T1RCR2.1: RJC: 1 = Rx JT-G704. */
					xlb[0x05] |= (1 << 7);	/* T1TCR1.7: TJC: 1 = Tx JT-G704. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = bit 2. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = bit 2. */
				}
				sp->status.config = 3;
				break;
			}
			/* fall through */
		case 3:	/* J1, ESF, AMI, CRC6J */
			if (ansi || !japan) {
				sp->config.ifgtype = SDL_GTYPE_T1;
				if (sp->config.ifframing != SDL_FRAMING_D4) {
					sp->config.ifframing = SDL_FRAMING_D4;
					xlb[0x04] &= ~(1 << 6);	/* T1RCR2.6: RFM: 0 = Rx D4. */
					xlb[0x07] &= ~(1 << 2);	/* T1CCR1.2: TFM: 0 = Tx D4. */
				}
				if (sp->config.ifcoding != SDL_CODING_B8ZS) {
					sp->config.ifcoding = SDL_CODING_B8ZS;
					xlb[0x04] |= (1 << 5);	/* T1RCR2.5: RB8ZS: 1 = Rx B8ZS. */
					xlb[0x06] |= (1 << 7);	/* T1TCR2.7: TB8ZS: 1 = Tx B8ZS. */
					xlb[0x41] |= (1 << 3);	/* ERCNT.3: VCRFS: 1 = count CVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
					sp->config.ifgcrc = SDL_GCRC_CRC6;
					xlb[0x04] &= ~(1 << 1);	/* T1RCR2.1: RJC: 0 = Rx ANSI CRC6. */
					xlb[0x05] &= ~(1 << 7);	/* T1TCR1.7: TJC: 0 = Tx ANSI CRC6. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = Rx ANSI. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = Tx ANSI. */
				}
				sp->status.config = 4;
				break;
			}
			/* fall through */
		case 4:	/* T1, D4, B8ZS, CRC6 */
			if (!ansi || japan) {
				sp->config.ifgtype = SDL_GTYPE_J1;
				if (sp->config.ifframing != SDL_FRAMING_D4) {
					sp->config.ifframing = SDL_FRAMING_D4;
					xlb[0x04] &= ~(1 << 6);	/* T1RCR2.6: RFM: 0 = Rx D4. */
					xlb[0x07] &= ~(1 << 2);	/* T1CCR1.2: TFM: 0 = Tx D4. */
				}
				if (sp->config.ifcoding != SDL_CODING_B8ZS) {
					sp->config.ifcoding = SDL_CODING_B8ZS;
					xlb[0x04] |= (1 << 5);	/* T1RCR2.5: RB8ZS: 1 = Rx B8ZS. */
					xlb[0x06] |= (1 << 7);	/* T1TCR2.7: TB8ZS: 1 = Tx B8ZS. */
					xlb[0x41] |= (1 << 3);	/* ERCNT.3: VCRFS: 1 = count CVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
					sp->config.ifgcrc = SDL_GCRC_CRC6J;
					xlb[0x04] |= (1 << 1);	/* T1RCR2.1: RJC: 1 = Rx JT-G704. */
					xlb[0x05] |= (1 << 7);	/* T1TCR1.7: TJC: 1 = Tx JT-G704. */
					xlb[0x04] |= (1 << 0);	/* T1RCR2.0: RD4YM: 1 = fm 12. */
					xlb[0x06] |= (1 << 2);	/* T1TCR2.2: TD4YM: 1 = fm 12. */
				}
				sp->status.config = 5;
				break;
			}
			/* fall through */
		case 5:	/* J1, D4, B8ZS, CRC6J */
			if (ansi || !japan) {
				sp->config.ifgtype = SDL_GTYPE_T1;
				if (sp->config.ifframing != SDL_FRAMING_D4) {
					sp->config.ifframing = SDL_FRAMING_D4;
					xlb[0x04] &= ~(1 << 6);	/* T1RCR2.6: RFM: 0 = Rx D4. */
					xlb[0x07] &= ~(1 << 2);	/* T1CCR1.2: TFM: 0 = Tx D4. */
				}
				if (sp->config.ifcoding != SDL_CODING_AMI) {
					sp->config.ifcoding = SDL_CODING_AMI;
					xlb[0x04] &= ~(1 << 5);	/* T1RCR2.5: RB8ZS: 0 = Rx AMI. */
					xlb[0x06] &= ~(1 << 7);	/* T1TCR2.7: TB8ZS: 0 = Tx AMI. */
					xlb[0x41] &= ~(1 << 3);	/* ERCNT.3: VCRFS: 0 = count BPVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
					sp->config.ifgcrc = SDL_GCRC_CRC6;
					xlb[0x04] &= ~(1 << 1);	/* T1RCR2.1: RJC: 0 = Rx ANSI CRC6. */
					xlb[0x05] &= ~(1 << 7);	/* T1TCR1.7: TJC: 0 = Tx ANSI CRC6. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = Rx ANSI. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = Tx ANSI. */
				}
				sp->status.config = 6;
				break;
			}
			/* fall through */
		case 6:	/* T1, D4, AMI, CRC6 */
			if (!ansi || japan) {
				sp->config.ifgtype = SDL_GTYPE_J1;
				if (sp->config.ifframing != SDL_FRAMING_D4) {
					sp->config.ifframing = SDL_FRAMING_D4;
					xlb[0x04] &= ~(1 << 6);	/* T1RCR2.6: RFM: 0 = Rx D4. */
					xlb[0x07] &= ~(1 << 2);	/* T1CCR1.2: TFM: 0 = Tx D4. */
				}
				if (sp->config.ifcoding != SDL_CODING_AMI) {
					sp->config.ifcoding = SDL_CODING_AMI;
					xlb[0x04] &= ~(1 << 5);	/* T1RCR2.5: RB8ZS: 0 = Rx AMI. */
					xlb[0x06] &= ~(1 << 7);	/* T1TCR2.7: TB8ZS: 0 = Tx AMI. */
					xlb[0x41] &= ~(1 << 3);	/* ERCNT.3: VCRFS: 0 = count BPVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
					sp->config.ifgcrc = SDL_GCRC_CRC6J;
					xlb[0x04] |= (1 << 1);	/* T1RCR2.1: RJC: 1 = Rx JT-G704. */
					xlb[0x05] |= (1 << 7);	/* T1TCR1.7: TJC: 1 = Tx JT-G704. */
					xlb[0x04] |= (1 << 0);	/* T1RCR2.0: RD4YM: 1 = fm 12. */
					xlb[0x06] |= (1 << 2);	/* T1TCR2.2: TD4YM: 1 = fm 12. */
				}
				sp->status.config = 7;
				break;
			}
			/* fall through */
		case 7:	/* J1, D4, AMI, CRC6J */
			if (ansi || !japan) {
				sp->config.ifgtype = SDL_GTYPE_T1;
				if (sp->config.ifframing != SDL_FRAMING_ESF) {
					sp->config.ifframing = SDL_FRAMING_ESF;
					xlb[0x04] |= (1 << 6);	/* T1RCR2.6: RFM: 1 = Rx ESF. */
					xlb[0x07] |= (1 << 2);	/* T1CCR1.2: TFM: 1 = Tx ESF. */
				}
				if (sp->config.ifcoding != SDL_CODING_B8ZS) {
					sp->config.ifcoding = SDL_CODING_B8ZS;
					xlb[0x04] |= (1 << 5);	/* T1RCR2.5: RB8ZS: 1 = Rx B8ZS. */
					xlb[0x06] |= (1 << 7);	/* T1TCR2.7: TB8ZS: 1 = Tx B8ZS. */
					xlb[0x41] |= (1 << 3);	/* ERCNT.3: VCRFS: 1 = count CVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6) {
					sp->config.ifgcrc = SDL_GCRC_CRC6;
					xlb[0x04] &= ~(1 << 1);	/* T1RCR2.1: RJC: 0 = Rx ANSI CRC6. */
					xlb[0x05] &= ~(1 << 7);	/* T1TCR1.7: TJC: 0 = Tx ANSI CRC6. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = Rx ANSI. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = Tx ANSI. */
				}
				sp->status.config = 0;
			} else {
				sp->config.ifgtype = SDL_GTYPE_J1;
				if (sp->config.ifframing != SDL_FRAMING_ESF) {
					sp->config.ifframing = SDL_FRAMING_ESF;
					xlb[0x04] |= (1 << 6);	/* T1RCR2.6: RFM: 1 = Rx ESF. */
					xlb[0x07] |= (1 << 2);	/* T1CCR1.2: TFM: 1 = Tx ESF. */
				}
				if (sp->config.ifcoding != SDL_CODING_B8ZS) {
					sp->config.ifcoding = SDL_CODING_B8ZS;
					xlb[0x04] |= (1 << 5);	/* T1RCR2.5: RB8ZS: 1 = Rx B8ZS. */
					xlb[0x06] |= (1 << 7);	/* T1TCR2.7: TB8ZS: 1 = Tx B8ZS. */
					xlb[0x41] |= (1 << 3);	/* ERCNT.3: VCRFS: 1 = count CVs. */
				}
				if (sp->config.ifgcrc != SDL_GCRC_CRC6J) {
					sp->config.ifgcrc = SDL_GCRC_CRC6J;
					xlb[0x04] |= (1 << 1);	/* T1RCR2.1: RJC: 1 = Rx JT-G704. */
					xlb[0x05] |= (1 << 7);	/* T1TCR1.7: TJC: 1 = Tx JT-G704. */
					xlb[0x04] &= ~(1 << 0);	/* T1RCR2.0: RD4YM: 0 = bit 2. */
					xlb[0x06] &= ~(1 << 2);	/* T1TCR2.2: TD4YM: 0 = bit 2. */
				}
				sp->status.config = 1;
			}
			return (1);
		}
		break;
	}
	return (0);
}

/** xp_x1_change_gtype: - change span type to or from E1 from or to T1/J1 or vise versa
  * @sp: span structure pointer
  * @xlb: span iobase
  *
  * Returns true (1) when a reconfiguration has been performed, and false (0), otherwise.
  */
noinline __unlikely int
xp_x1_change_gtype(struct sp *sp, register volatile uint8_t *xlb)
{
	struct ch *ch;
	int chan, offset;

	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		if (!etsi) {
			/* We are not directed in the E1 configuration by driver parameters, sot the setting
			   should likely be T1 or J1. Reconfigure T1 or J1, do a LIRST and go back to the
			   start. */
			/* remove interrupt mask for E1 mode */
			xlb[0x19] &= ~((1 << 7) | (1 << 3));
			xlb[0x1b] &= ~((1 << 2) | (1 << 1) | (1 << 0));
			xlb[0x1d] &= ~((1 << 6) | (1 << 5));
			/* first set up all T1/J1 registers */
			/* need to change from E1 to T1 */
			sp->config = sdl_default_t1_span;
			for (chan = 1; chan < 24; chan++) {
				if ((ch = sp->chans[chan]) != NULL) {
					ch->option = lmi_default_t1_chan;
					ch->sdl.config = sdl_default_t1_chan;
					ch->sdt.config = sdt_default_t1_chan;
					ch->sl.config = sl_default_t1_chan;
					xp_x1_chan_config(sp, ch, xlb);
					continue;
				}
				swerr();
			}
			/* set up line interface */
			xlb[0x78] = (xlb[0x78] & ~((0x7 << 5) | (1 << 4))) | (0x00 << 5) | (0 << 4);	/* LIC1 
													 */
			xlb[0x79] |= (1 << 3);	/* LIC2.3: 1 = JAMUX: 1 = PLL 2.048 at MCLK.  */
			// xlb[0x7a] = xlb[0x7a]; /* LIC3 */ /* leave linear gain alone */
			xlb[0x7c] = 0x00;	/* Unused */
			xlb[0x7d] = 0x00;	/* TLBC: Automatic gain control. */
			xlb[0x7e] = (1 << 7) | (1 << 6);	/* IAAR.7(GTIC), IAAR.6(GRIC) */
			xlb[0x7f] = 0x7f;	/* PCICR set idle code 0xff */
			/* next switch to T1 mode */
			xlb[0x00] &= ~(1 << 1);	/* MSTRREG.1=0: T1/J1 mode. */
			xlb[0x79] &= ~(1 << 7);	/* LIC2.7: 0 = T1 */
			xlb[0x7b] = (0x00 << 4) | (0x02 << 2) | (0x02 << 0);	/* 100 Ohm term, MCLK 2.048
										   MHz */
			/* last zero all E1 registers */
			/* 0x33 to 0x36 are E1 Tx and Rx control registers */
			for (offset = 0; offset < 4; offset++)
				xlb[0x33 + offset] = 0x00;
			/* except maybe these two */
			xlb[0xd0] = 0x1b;	/* TAF.0-7='00011011'B (default) */
			xlb[0xd1] = 0x40;	/* TNAF.0-7='01000000'B (default) */
			/* apply interrupt mask for T1/J1 mode */
			xlb[0x1b] |= (1 << 7) | (1 << 6) | (1 << 5);
			xlb[0x1d] |= (1 << 7);
			/* FIXME: do a LIRST to reset the JALT */
			return (1);
		}
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		if ((sp->config.ifgtype == SDL_GTYPE_T1 && !ansi)
		    || (sp->config.ifgtype == SDL_GTYPE_J1 && !japan)) {
			/* We are not directed in the T1 or J1 configuration by driver parameters, so the
			   setting should likely be E1.  Reconfigure E1, do a LIRST and go back to the start. 
			 */
			/* remove interrupt mask for T1/J1 mode */
			xlb[0x1b] &= ~((1 << 7) | (1 << 6) | (1 << 5));
			xlb[0x1d] &= ~((1 << 7));
			/* first set up all E1 registers */
			xlb[0xd0] = 0x1b;	/* TAF.0-7='00011011'B (default) */
			xlb[0xd1] = 0x40;	/* TNAF.0-7='01000000'B (default) */
			xlb[0x50] = (1 << 3) | (1 << 1) | (1 << 0);	/* TS1: X=1, Y=0, CAS align word */
			/* need to change from T1/J1 to E1 */
			sp->config = sdl_default_e1_span;
			for (chan = 1; chan < 32; chan++) {
				if ((ch = sp->chans[chan]) != NULL) {
					ch->option = lmi_default_e1_chan;
					ch->sdl.config = sdl_default_e1_chan;
					ch->sdt.config = sdt_default_e1_chan;
					ch->sl.config = sl_default_e1_chan;
					xp_x1_chan_config(sp, ch, xlb);
					continue;
				}
				swerr();
			}
			/* set up line interface */
			xlb[0x78] = (xlb[0x78] & ~((0x7 << 5) | (1 << 4))) | (0x01 << 5) | (1 << 4);	/* LIC1 
													 */
			xlb[0x79] |= (1 << 3);	/* LIC2.3: 1 = JAMUX: 1 = PLL 2.048 at MCLK.  */
			// xlb[0x7a] = xlb[0x7a]; /* LIC3 */ /* leave linear gain alone */
			xlb[0x7c] = 0x00;	/* Unused */
			xlb[0x7d] = 0x00;	/* TLBC: Automatic gain control. */
			xlb[0x7e] = (1 << 7) | (1 << 6);	/* IAAR.7(GTIC), IAAR.6(GRIC) */
			xlb[0x7f] = 0xff;	/* PCICR set idle code 0xff */
			/* next switch to E1 mode */
			xlb[0x00] |= (1 << 1);	/* MSTRREG.1=1: E1 mode. */
			xlb[0x79] |= (1 << 7);	/* LIC2.7: 1 = E1. */
			xlb[0x7b] = (0x00 << 4) | (0x03 << 2) | (0x03 << 0);	/* 120 Ohm term, MCLK 2.048
										   MHz */
			/* last zero all T1/J1 registers */
			/* 0x03 to 0x07 are T1 Tx and Rx control registers */
			for (offset = 0; offset < 5; offset++)
				xlb[0x03 + offset] = 0x00;
			/* 0xb6 to 0xbf are programmable in-band loop code generatiion and detection that are 
			   only applicable to T1. */
			for (offset = 0; offset < 10; offset++)
				xlb[0xb6 + offset] = 0x00;
			/* apply interrupt mask for E1 mode */
			xlb[0x19] |= (1 << 7) | (1 << 3);
			xlb[0x1b] |= (1 << 2) | (1 << 1) | (1 << 0);
			xlb[0x1d] |= (1 << 6) | (1 << 5);
			/* FIXME: do a LIRST to reset the JALT */
			return (1);
		}
		break;
	}
	return (0);
}

/** xp_x1_process_stats: - process statistics (performance measures) for the span
  * @sp: span structure pointer
  * @errors: error events accumulated so far.
  *
  * ERROR EVENTS:
  * Bipolar Violation BPV Error Event: A BPV error event for an AMI coded signal is the occurrence
  *	of a pulse of the same polarity as the previous pulse.  A BPV error event for a B8ZS or HDB3
  *	coded signal is the occurrence of a pulse of the same polarity as the previous pulse without
  *	being part of the zero substitution code. 
  *
  * Controlled Slip (CS) Error Event: A CS is the replication or deletion of the payload bits of a
  *	DS1 frame.  A CS may be performed when there is a difference between the timing of a
  *	synchronous receiving terminal an the received signal.  A CS does not acuase an Out of Frame
  *	defect. 
  *
  * Excessive Zeros (EXZ) Error Event: An EXZ error event for an AMI coded signal is the occurence
  *	of more than fifteen contiguous zeros.  For a B8ZS coded signal, the defect occurs when more
  *	than seven contiguous zeros are detected. 
  *
  * Line Coding Violation (LCV) Error Event: An LCV is the occurence of either a Bipolar Violation
  *	or Excessive Zeros (EXZ) error event. 
  *
  * Path Coding Violation (PCV) Error Event: A PCV error event is a frame synchronization bit error
  *	in the D4 and E1-non-CRC-4 formats, or a CRC error in the ESF and E1-CRC-4 formats. 
  *
  * PERFORMANCE PARAMETERS:
  * All performance parameters are accumulated in fifteen minute intervals and up to 96 intervals
  * (covering a 24-hour period) are kept by an agent.  Fewer than 96 intervals of data will be
  * available if the agent has been restarted within the last 24 hours.  In addition, there is a
  * rolling 24-hour total of each performance parameter.  There is no requirement for an agent to
  * ensure a fixed relationship between the start of a fifteen minute interval and clock time;
  * however, some agents may align the fifteen minute intervals with quarter hours. 
  *
  * Severely Errored Seconds (SES): An SES for ESF signals is a second with one of the following:
  *	320 or more PCV error events; one or more OOF defects; a detected AIS defect. For E1 CRC-4
  *	signals, an SES is a second with either 832 or more PCV error events or one or more OOF
  *	defects.  For E1 non-CRC-4 signals, an SES is a 2048 (LCV ????) PCV or more. For D4 signals,
  *	an SES is a count of one-second intervals with Framing Error events, or an OOF defect, or
  *	1544 LCV or more.  Controlled slips are not included in this parameter. This is not
  *	incremented during an Unavailable Second.
  *
  * Severely Errored Framing Seconds (SEFS): An SEFS is a second with either one or more OOF
  *	defects, or a detected AIS defect.
  *
  * Unavailable Seconds (UASs):  A performance management event that is calculted by counting the
  *	number of seconds for which the interface is unavailable.  An interface is said to be
  *	unavailable from the onset of 10 continugous Severely Errored Seconds (SESs), or on the
  *	onset of a condition leading to a failutre. Once unavailable, and if no failure is present,
  *	an interface becomes available at the onset of 10 contiguous seconds with no SESs. 
  *
  *	UAS are calculated by counting the number of seconds that the interface is unavailable.  The
  *	DS1 interface is said to be unavailable from the onset of ten contiguous SESs, or the onset
  *	of the condition leadeing to a failure (see Failure States).  If the condition leading to
  *	the failure was immediately preceded by one or more continguous SES, then the DS1 interface
  *	unavailability starts from the onset of these SES. Once unavailable, and if no failure is
  *	present, the DS1 interfae becomes available at the onset of 10 contiguous seconds with no
  *	SES.  if the failure clearing time is less than or equat to ten seconds.  If the failure
  *	clearing time is more than ten seconds, the DS1 interface becoms available at the onset of
  *	tem contiguous seconds with no SES, or the onset period leading to the successful clearing
  *	condition, whichever occurs later.  With respect to DS1 error counts, all coutners are
  *	incremented while the DS1 interface is deemed available.  While the interface is deemed
  *	unavailable, the only count that is incremented is UAS.  A special case exists when the ten
  *	or more second period crosses the 900 second statistic window boundary, as the foregoing
  *	description ipmlies that the SES and UAS counter must be adjusted when the UAS stats is
  *	entered.  Successive "gets" of the affectetd dsx1IntervalSESs and dsx1IntervalUASs objects
  *	will return differing values if the first get occurs during the first few seconds of the
  *	window.  This is viewed as an unavoidable side-effect of selecting the presently-defined
  *	objects.  
  *
  * Bursty Errored Secods (BES): A BES (also known as an Errored Second Type B) is a second with
  *	fewer than 320 and more than one PCV, no SEF defects, and no detected incoming AIS defects.
  *	Controlled slips are not incldued in this parameter. 
  *
  * Errored Seconds (ES): For ESF and E1-CRC links an Errored Second is a second with one of the
  *	followng: one or more PCV; one or more OOF defects; one or more Controlled Slip events; a
  *	detected AIS defect.
  *
  * Controlled Slip Seconds (CSS): A cCSS is a one-second interval containing one or more controlled
  *	slips.
  *
  * Line Errored Seconds (LES): A LES, according to T1M1.3, is a second in which one or more LCV
  *	error events were detected.  While many implementations are currently unable to detect the
  *	zero strings, it is expected that interface manufacturers will add this capability in
  *	deference to ANSI; therefore, it will become available in time. In the T1M1.3 specification,
  *	near end LCV and far end LES are counted.  For consistency, we count LES at both ends.
  *
  * Degraded Minutes: A DM is one in which the estimated error rate exceeds 1E-06 but does not
  *	exceed 1E-03.  DM are determined by collecting all of the Available Seconds, removing any
  *	Severely Errored Seconds grouping the result in 60-second long groups and counting a
  *	60-second long group (minute) as degraded if the cumulative errors during the seconds
  *	present in the group exceed 1E-6.  Available seconds are merely those seconds which are not
  *	unavailable as described below. 
  */
static noinline fastcall __hot void
xp_x1_process_stats(struct sp *sp, register uint errors)
{
	/* This is a one second timeout */

	/* Calculate severely errored seconds. */
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		switch (sp->config.ifgcrc) {
		case SDL_GCRC_CRC4:
			/* For E1 CRC4 signals, an SES is 832 or more PCV error events, or one or more Out of 
			   Frame defects. */
			if (sp->stats[0].PCVs >= 832)
				errors |= XP_ERR_SES;
			break;
		case SDL_GCRC_CRC5:
			/* For E1 non-CRC4 signals, an SES is 2048 or more LCV. */
			if (sp->stats[0].LCVs >= 2048)
				errors |= XP_ERR_SES;
			break;
		}
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		/* Note: when there are a lot of PCV and the span is T1, there is possibility that it should
		   be set to J1 instead.  When there are a lot of PCV and the span is J1, there is a
		   possibility that it should be set to T1 instead. */
		switch (sp->config.ifframing) {
		case SDL_FRAMING_ESF:
			/* A Severely Errored Second for ESF signals is a second with one of the following:
			   320 or more PCV error events; one or more OOF defects; or a detected AIS defect. */
			if (sp->stats[0].PCVs >= 320)
				errors |= XP_ERR_SES;
			break;
		case SDL_FRAMING_D4:
			/* For D4 signals, an SES is a count of one-second intervals with Framing Error
			   events, or an Out of Frame defect; or 1544 or more LCVs. */
			if (sp->stats[0].LCVs >= 1544)
				errors |= XP_ERR_SES;
			break;
		}
		break;
	}

	/* The number of Unavailable Seconds (UASs) in the current interval. */
	/* Track the number of consecutive severely errored seconds and the number of consecutive
	   non-severely errored seconds */
	if (errors & (XP_ERR_SES)) {
		sp->status.nses = 0;
		sp->status.sess++;
		if (sp->status.sess >= 10) {
			if (sp->status.sess == 10 && !(errors & XP_ERR_UAS)) {
				sp->stats[0].UASs += 9;
				errors |= XP_ERR_UAS;
			}
			errors |= XP_ERR_UAS;
		}
	} else {
		sp->status.sess = 0;
		sp->status.nses++;
		if (sp->status.nses >= 10) {
			if (sp->status.nses == 10 && (errors & XP_ERR_UAS)) {
				sp->stats[0].UASs -= 9;
				errors &= ~XP_ERR_UAS;
			}
			errors &= ~XP_ERR_UAS;
		}
	}
	if (errors & (XP_ERR_UAS))
		sp->stats[0].UASs = 1;

	/* Calculate bursty errored seconds */
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		switch (sp->config.ifgcrc) {
		case SDL_GCRC_CRC4:
			if (1 < sp->stats[0].PCVs && sp->stats[0].PCVs < 832)
				if (!(errors & XP_ERR_SEFS))
					if (!(errors & XP_FAIL_AIS))
						errors |= XP_ERR_BES;
			break;
		case SDL_GCRC_CRC5:
			if (1 < sp->stats[0].LCVs && sp->stats[0].LCVs < 2048)
				if (!(errors & XP_ERR_SEFS))
					if (!(errors & XP_FAIL_AIS))
						errors |= XP_ERR_BES;
			break;
		}
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		switch (sp->config.ifframing) {
		case SDL_FRAMING_ESF:
			if (1 < sp->stats[0].PCVs && sp->stats[0].PCVs < 320)
				if (!(errors & XP_ERR_SEFS))
					if (!(errors & XP_FAIL_AIS))
						errors |= XP_ERR_BES;
			break;
		case SDL_FRAMING_D4:
			if (1 < sp->stats[0].LCVs && sp->stats[0].LCVs < 1544)
				if (!(errors & XP_ERR_SEFS))
					if (!(errors & XP_FAIL_AIS))
						errors |= XP_ERR_BES;
			break;
		}
		break;
	}

	/* The number of Errored Seconds (ESs) in the current interval. */
	if (sp->stats[0].PCVs > 0)
		errors |= XP_ERR_ES;
	if (errors & (XP_ERR_ES))
		sp->stats[0].ESs = 1;
	/* The number of severely errored seconds (SESs) in the current interval. */
	if (errors & (XP_ERR_SES))
		sp->stats[0].SESs = 1;
	/* The number of Severely Errored Framing Seconds (SEFSs) in the current interval. */
	if (errors & (XP_ERR_SEFS))
		sp->stats[0].SEFSs = 1;
	/* The number of controlled slip seconds (CSSs) in the current interval. */
	if (errors & (XP_ERR_CSS))
		sp->stats[0].CSSs = 1;
	/* The number of Line Errored Seconds (LESs) in the current interval. */
	if (errors & (XP_ERR_LES))
		sp->stats[0].LESs = 1;
	/* The number of Bursty Errored Seconds (BESs) in the current interval. */
	if (errors & (XP_ERR_BES))
		sp->stats[0].BESs = 1;

	/* accumulate the one-second counts */
	sp->stats[1].ESs += sp->stats[0].ESs;
	sp->stats[1].SESs += sp->stats[0].SESs;
	sp->stats[1].ESs += sp->stats[0].ESs;
	sp->stats[1].SESs += sp->stats[0].SESs;
	sp->stats[1].SEFSs += sp->stats[0].SEFSs;
	sp->stats[1].UASs += sp->stats[0].UASs;
	sp->stats[1].CSSs += sp->stats[0].CSSs;
	sp->stats[1].PCVs += sp->stats[0].PCVs;
	sp->stats[1].LESs += sp->stats[0].LESs;
	sp->stats[1].BESs += sp->stats[0].BESs;
	sp->stats[1].DMs += sp->stats[0].DMs;
	sp->stats[1].LCVs += sp->stats[0].LCVs;
	sp->stats[1].FASEs += sp->stats[0].FASEs;
	sp->stats[1].FABEs += sp->stats[0].FABEs;
	sp->stats[1].FEBEs += sp->stats[0].FEBEs;
	bzero(&sp->stats[0], sizeof(sp->stats[0]));

	if ((++sp->stats[1].SECs) == 60) {
		/* The number of degraded minutes (DMs) in the current interval. */
		if (errors & (XP_ERR_DM))
			sp->stats[1].DMs = 1;

		/* accumulate one-minute counts */
		sp->stats[2].ESs += sp->stats[1].ESs;
		sp->stats[2].SESs += sp->stats[1].SESs;
		sp->stats[2].ESs += sp->stats[1].ESs;
		sp->stats[2].SESs += sp->stats[1].SESs;
		sp->stats[2].SEFSs += sp->stats[1].SEFSs;
		sp->stats[2].UASs += sp->stats[1].UASs;
		sp->stats[2].CSSs += sp->stats[1].CSSs;
		sp->stats[2].PCVs += sp->stats[1].PCVs;
		sp->stats[2].LESs += sp->stats[1].LESs;
		sp->stats[2].BESs += sp->stats[1].BESs;
		sp->stats[2].DMs += sp->stats[1].DMs;
		sp->stats[2].LCVs += sp->stats[1].LCVs;
		sp->stats[2].FASEs += sp->stats[1].FASEs;
		sp->stats[2].FABEs += sp->stats[1].FABEs;
		sp->stats[2].FEBEs += sp->stats[1].FEBEs;
		bzero(&sp->stats[1], sizeof(sp->stats[1]));

		if ((sp->stats[2].SECs += 60) == 900) {
			uint index = sp->curr;

			sp->hist[index] = sp->stats[2];
			sp->hist[index].ValidData = 2;	/* true(2) */
			bzero(&sp->stats[2], sizeof(sp->stats[2]));
			if (++index == 96)
				index = 0;
			sp->curr = index;
		}
	}
	return;
}

static noinline fastcall __hot void
xp_x1_process_alarms(struct cd *cd, struct sp *sp, register volatile uint8_t *xlb, register uint flags,
		     uint timeout)
{
	register uint alarms = sp->config.ifalarms;

	/* integrate alarms */
	if ((flags & SDL_IF_RX_RDMA) && (sp->config.ifframing == SDL_FRAMING_CAS))
		alarms |= SDL_ALARM_DMF;
	else
		alarms &= ~SDL_ALARM_DMF;
	if (flags & (SDL_IF_RX_RRA | SDL_IF_RX_RYEL))
		alarms |= SDL_ALARM_YEL;
	else
		alarms &= ~SDL_ALARM_YEL;
	if (flags & SDL_IF_RX_RUA1)
		alarms |= SDL_ALARM_BLU;
	else
		alarms &= ~SDL_ALARM_BLU;
	if (flags & (SDL_IF_RX_RLOS | SDL_IF_RX_FRCL | SDL_IF_RX_LRCL))
		alarms |= SDL_ALARM_RED;
	else
		alarms &= ~SDL_ALARM_RED;
	if ((alarms & SDL_ALARM_REC) && (timeout)) {
		if (alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
			/* can't recover, alarms started again */
			alarms &= ~SDL_ALARM_REC;
		else {
			/* recovery time is in terms of timeouts (5 seconds) */
			if (sp->recovertime && !--sp->recovertime) {
				if (sp->config.ifgtype == SDL_GTYPE_E1) {
					/* Not required as we are setting automatic RAI generation, but it
					   cannot hurt. */
					xlb[0xd1] &= ~(1 << 5);	/* TNAF.5: A: 0 = no RAI */
				} else {
					xlb[0x05] &= ~(1 << 0);	/* T1RCR1.0: TYEL: 0 = no YEL */
				}
				alarms &= ~SDL_ALARM_REC;
				cd->eval_syncsrc = 1;
			}
		}
	}
	/* if any of the alarms (or recovery) changed we need to do this */
	if ((alarms ^ sp->config.ifalarms)
	    & (SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC)) {
		if ((alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
		    && !(sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) {
			/* Local alarms have just begun.  Signal a yellow (RAI) alarm to the other end. */
			if (!(alarms & SDL_ALARM_REC)) {
				/* local alarms just begun and we were not in recovery */
				if (sp->config.ifgtype == SDL_GTYPE_E1) {
					/* Not required as we are setting automatic RAI generation, but it
					   cannot hurt. */
					xlb[0xd1] |= (1 << 5);	/* TNAF.5: A: 1 = RAI */
				} else {
					xlb[0x05] |= (1 << 0);	/* T1TCR1.0: TYEL: 1 = transmit yellow alarm */
				}
				if (alarms & SDL_ALARM_RED)
					cd->eval_syncsrc = 1;
			} else
				alarms &= ~SDL_ALARM_REC;
		} else if ((alarms & SDL_ALARM_RED)
			   && !(sp->config.ifalarms & SDL_ALARM_RED)) {
			/* red alarm just got added to the set, reevaluate the sync source */
			cd->eval_syncsrc = 1;
		} else if (!(alarms & (SDL_ALARM_RED | SDL_ALARM_BLU))
			   && (sp->config.ifalarms & (SDL_ALARM_RED | SDL_ALARM_BLU))) {
			/* Local alarms have just ended. */
			alarms |= SDL_ALARM_REC;
			if (xlb[0x41] & 0x20) {
				if (sp->config.ifgtype == SDL_GTYPE_E1)
					sp->recovertime = X400P_SDL_ALARM_SETTLE_E1;
				else
					sp->recovertime = X400P_SDL_ALARM_SETTLE_T1;
			} else
				sp->recovertime = X400P_SDL_ALARM_SETTLE_SECONDS;
		}
		sp->config.ifalarms &= ~(SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC);
		sp->config.ifalarms |=
		    (alarms & (SDL_ALARM_RED | SDL_ALARM_BLU | SDL_ALARM_YEL | SDL_ALARM_REC));

		{
			int leds = 0, all_leds;

			/* adjust leds */
			if (alarms & SDL_ALARM_RED)
				leds |= LEDRED;
			else if (alarms & SDL_ALARM_YEL)
				leds |= LEDYEL;
			else
				leds |= LEDGRN;
			all_leds = cd->ledreg;
			all_leds &= ~(LEDYEL << (sp->span << 1));
			all_leds |= (leds << (sp->span << 1));
			if (cd->ledreg != all_leds) {
				cd->ledreg = all_leds;
				cd->xlb[LEDREG] = cd->ledreg;
			}
		}
	}
}

/** xp_x1_eval_syncsrc: - reevalute the synchronization source
  * @cd: card structur pointer
  *
  * There is really only one version of this function, it is just laid out in several places for
  * proper position with respect to the main interrupt service routine.
  */
static noinline fastcall __hot void
xp_x1_eval_syncsrc(struct cd *cd)
{
	/* for safety */
	if (cd->config.ifsyncsrc[0] != 0) {
		int src, synreg = SYNCSELF, ctlreg, clkreg = cd->clkreg;

		for (src = 0; src < SDL_SYNCS; src++) {
			struct sp *sp;
			int span;

			if ((span = cd->config.ifsyncsrc[src]) == 0)
				break;
			if ((--span < cd->ports && span >= 0)
			    && (sp = cd->spans[span])
			    && (sp->config.ifflags & SDL_IF_UP)
			    && (sp->config.ifflags & SDL_IF_RX_UP)
			    && (sp->config.ifflags & SDL_IF_RX_RUNNING)
			    && (sp->config.ifclock != SDL_CLOCK_LOOP)
			    && !(sp->config.ifalarms & (SDL_ALARM_BLU | SDL_ALARM_RED))) {
				synreg = cd->config.ifsyncsrc[src];
				if (sp->config.ifgtype == SDL_GTYPE_E1)
					clkreg |= E1DIV;
				else
					clkreg &= ~E1DIV;
				break;
			}
		}
		if (cd->config.ifsync != synreg) {
			cd->config.ifsync = synreg;
			ctlreg = cd->ctlreg & ~E1DIV;
			ctlreg |= clkreg;	/* use divider for span or clock */
			cd->xlb[CTLREG] = cd->ctlreg = ctlreg;
			cd->xlb[SYNREG] = cd->synreg = synreg;
		}
	}
}

/*
 *  =========================================================================
 *
 *  STREAMS Message Handling
 *
 *  =========================================================================
 *
 *  M_IOCTL Handling
 *  -------------------------------------------------------------------------
 */

noinline fastcall __unlikely int
dsx_copyin2(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
	case _IOC_NR(DSX_IOCGINFO):	/* dsx_info_t */
	case _IOC_NR(DSX_IOCGOPTION):	/* dsx_option_t */
		goto eproto;
	case _IOC_NR(DSX_IOCSOPTION):	/* dsx_option_t */
		err = dsx_iocsoption(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCLCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCGCONFIG):	/* dsx_config_t */
		goto eproto;
	case _IOC_NR(DSX_IOCSCONFIG):	/* dsx_config_t */
		err = dsx_iocsconfig(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCTCONFIG):	/* dsx_config_t */
		err = dsx_ioctconfig(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCCONFIG):	/* dsx_config_t */
		err = dsx_ioccconfig(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGSTATEM):	/* dsx_statem_t */
		goto eproto;
	case _IOC_NR(DSX_IOCCMRESET):	/* dsx_statem_t */
		err = dsx_ioccmreset(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGSTATUS):	/* dsx_status_t */
		goto eproto;
	case _IOC_NR(DSX_IOCSSTATUS):	/* dsx_status_t */
		err = dsx_iocsstatus(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCSTATUS):	/* dsx_status_t */
		err = dsx_ioccstatus(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGSTATSP):	/* dsx_stats_t */
	case _IOC_NR(DSX_IOCGSTATS):	/* dsx_stats_t */
		goto eproto;
	case _IOC_NR(DSX_IOCSSTATSP):	/* dsx_stats_t */
		err = dsx_iocsstatsp(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCSTATS):	/* dsx_stats_t */
		err = dsx_ioccstats(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGNOTIFY):	/* dsx_notify_t */
		goto eproto;
	case _IOC_NR(DSX_IOCSNOTIFY):	/* dsx_notify_t */
		err = dsx_iocsnotify(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCNOTIFY):	/* dsx_notify_t */
		err = dsx_ioccnotify(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGATTR):	/* dsx_attr_t */
		goto eproto;
	case _IOC_NR(DSX_IOCCMGMT):	/* dsx_mgmt_t */
		err = dsx_ioccmgmt(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCPASS):	/* dsx_pass_t */
		err = dsx_ioccpass(q, mp, dp);
		break;
	default:
		goto einval;
		break;
	}
	return (err);
      eproto:
	__swerr();
	return (-EPROTO);
      einval:
	return (-EINVAL);
}

/** dsx_copyin: - first copyin stage for DSX_IOC
  *
  * This function must return <0, error; 0, copyout or copydone performed, >0, additional copyin
  * required.
  */
noinline fastcall __unlikely int
dsx_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
	case _IOC_NR(DSX_IOCGINFO):	/* dsx_info_t */
		err = dsx_iocginfo(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGOPTION):	/* dsx_option_t */
		err = dsx_iocgoption(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCSOPTION):	/* dsx_option_t */
		err = dsx_option_size((dsx_option_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCLCONFIG):	/* dsx_config_t */
		err = dsx_ioclconfig(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGCONFIG):	/* dsx_config_t */
		err = dsx_iocgconfig(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCSCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCTCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCCCONFIG):	/* dsx_config_t */
		err = dsx_config_size((dsx_config_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCGSTATEM):	/* dsx_statem_t */
		err = dsx_iocgstatem(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCMRESET):	/* dsx_statem_t */
		err = dsx_statem_size((dsx_statem_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCGSTATUS):	/* dsx_status_t */
		err = dsx_iocgstatus(q, mp, dp);
	case _IOC_NR(DSX_IOCSSTATUS):	/* dsx_status_t */
	case _IOC_NR(DSX_IOCCSTATUS):	/* dsx_status_t */
		err = dsx_status_size((dsx_status_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCGSTATSP):	/* dsx_stats_t */
		err = dsx_iocgstatsp(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCGSTATS):	/* dsx_stats_t */
		err = dsx_iocgstats(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCSSTATSP):	/* dsx_stats_t */
	case _IOC_NR(DSX_IOCCSTATS):	/* dsx_stats_t */
		err = dsx_stats_size((dsx_stats_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCGNOTIFY):	/* dsx_notify_t */
		err = dsx_iocgnotify(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCSNOTIFY):	/* dsx_notify_t */
	case _IOC_NR(DSX_IOCCNOTIFY):	/* dsx_notify_t */
		err = dsx_notify_size((dsx_notify_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCGATTR):	/* dsx_attr_t */
		err = dsx_iocgattr(q, mp, dp);
		break;
	case _IOC_NR(DSX_IOCCMGMT):	/* dsx_mgmt_t */
		err = dsx_mgmt_size((dsx_mgmt_t *) dp->b_rptr);
		break;
	case _IOC_NR(DSX_IOCCPASS):	/* dsx_pass_t */
		err = dsx_pass_size((dsx_pass_t *) dp->b_rptr);
		break;
	default:
		err = -EINVAL;
	}
	return (err);
}

noinline fastcall __unlikely int
dsx_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(DSX_IOCGINFO):	/* dsx_info_t */
		err = sizeof(dsx_info_t);
		break;
	case _IOC_NR(DSX_IOCGOPTION):	/* dsx_option_t */
	case _IOC_NR(DSX_IOCSOPTION):	/* dsx_option_t */
		err = sizeof(dsx_option_t);
		break;
	case _IOC_NR(DSX_IOCLCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCGCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCSCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCTCONFIG):	/* dsx_config_t */
	case _IOC_NR(DSX_IOCCCONFIG):	/* dsx_config_t */
		err = sizeof(dsx_config_t);
		break;
	case _IOC_NR(DSX_IOCGSTATEM):	/* dsx_statem_t */
	case _IOC_NR(DSX_IOCCMRESET):	/* dsx_statem_t */
		err = sizeof(dsx_statem_t);
		break;
	case _IOC_NR(DSX_IOCGSTATUS):	/* dsx_status_t */
	case _IOC_NR(DSX_IOCSSTATUS):	/* dsx_status_t */
	case _IOC_NR(DSX_IOCCSTATUS):	/* dsx_status_t */
		err = sizeof(dsx_status_t);
		break;
	case _IOC_NR(DSX_IOCGSTATSP):	/* dsx_stats_t */
	case _IOC_NR(DSX_IOCSSTATSP):	/* dsx_stats_t */
	case _IOC_NR(DSX_IOCGSTATS):	/* dsx_stats_t */
	case _IOC_NR(DSX_IOCCSTATS):	/* dsx_stats_t */
		err = sizeof(dsx_stats_t);
		break;
	case _IOC_NR(DSX_IOCGNOTIFY):	/* dsx_notify_t */
	case _IOC_NR(DSX_IOCSNOTIFY):	/* dsx_notify_t */
	case _IOC_NR(DSX_IOCCNOTIFY):	/* dsx_notify_t */
		err = sizeof(dsx_notify_t);
		break;
	case _IOC_NR(DSX_IOCGATTR):	/* dsx_attr_t */
		err = sizeof(dsx_attr_t);
		break;
	case _IOC_NR(DSX_IOCCMGMT):	/* dsx_mgmt_t */
		err = sizeof(dsx_mgmt_t);
		break;
	case _IOC_NR(DSX_IOCCPASS):	/* dsx_pass_t */
		err = sizeof(dsx_pass_t);
		break;
	default:
		err = -EINVAL;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
mx_copyin2(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
	case _IOC_NR(MX_IOCGINFO):	/* mx_info_t */
	case _IOC_NR(MX_IOCGOPTION):	/* mx_option_t */
		goto eproto;
	case _IOC_NR(MX_IOCSOPTION):	/* mx_option_t */
		err = mx_iocsoption(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCLCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCGCONFIG):	/* mx_config_t */
		goto eproto;
	case _IOC_NR(MX_IOCSCONFIG):	/* mx_config_t */
		err = mx_iocsconfig(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCTCONFIG):	/* mx_config_t */
		err = mx_ioctconfig(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCCONFIG):	/* mx_config_t */
		err = mx_ioccconfig(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGSTATEM):	/* mx_statem_t */
		goto eproto;
	case _IOC_NR(MX_IOCCMRESET):	/* mx_statem_t */
		err = mx_ioccmreset(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGSTATUS):	/* mx_status_t */
		goto eproto;
	case _IOC_NR(MX_IOCSSTATUS):	/* mx_status_t */
		err = mx_iocsstatus(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCSTATUS):	/* mx_status_t */
		err = mx_ioccstatus(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGSTATSP):	/* mx_stats_t */
		goto eproto;
	case _IOC_NR(MX_IOCGSTATS):	/* mx_stats_t */
		goto eproto;
	case _IOC_NR(MX_IOCSSTATSP):	/* mx_stats_t */
		err = mx_iocsstatsp(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCSTATS):	/* mx_stats_t */
		err = mx_ioccstats(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGNOTIFY):	/* mx_notify_t */
		goto eproto;
	case _IOC_NR(MX_IOCSNOTIFY):	/* mx_notify_t */
		err = mx_iocsnotify(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCNOTIFY):	/* mx_notify_t */
		err = mx_ioccnotify(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGATTR):	/* mx_attr_t */
		goto eproto;
	case _IOC_NR(MX_IOCCMGMT):	/* mx_mgmt_t */
		err = mx_ioccmgmt(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCPASS):	/* mx_pass_t */
		err = mx_ioccpass(q, mp, dp);
		break;
	default:
		goto einval;
	}
	return (err);
      eproto:
	__swerr();
	return (-EPROTO);
      einval:
	return (-EINVAL);
}

noinline fastcall __unlikely int
mx_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
	case _IOC_NR(MX_IOCGINFO):	/* mx_info_t */
		err = mx_iocginfo(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGOPTION):	/* mx_option_t */
		err = mx_iocgoption(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCSOPTION):	/* mx_option_t */
		err = mx_option_size((mx_option_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCLCONFIG):	/* mx_config_t */
		err = mx_ioclconfig(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGCONFIG):	/* mx_config_t */
		err = mx_iocgconfig(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCSCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCTCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCCCONFIG):	/* mx_config_t */
		err = mx_config_size((mx_config_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCGSTATEM):	/* mx_statem_t */
		err = mx_iocgstatem(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCMRESET):	/* mx_statem_t */
		err = mx_statem_size((mx_statem_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCGSTATUS):	/* mx_status_t */
		err = mx_iocgstatus(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCSSTATUS):	/* mx_status_t */
	case _IOC_NR(MX_IOCCSTATUS):	/* mx_status_t */
		err = mx_status_size((mx_status_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCGSTATSP):	/* mx_stats_t */
		err = mx_iocgstatsp(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCGSTATS):	/* mx_stats_t */
		err = mx_iocgstats(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCSSTATSP):	/* mx_stats_t */
	case _IOC_NR(MX_IOCCSTATS):	/* mx_stats_t */
		err = mx_stats_size((mx_stats_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCGNOTIFY):	/* mx_notify_t */
		err = mx_iocgnotify(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCSNOTIFY):	/* mx_notify_t */
	case _IOC_NR(MX_IOCCNOTIFY):	/* mx_notify_t */
		err = mx_notify_size((mx_notify_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCGATTR):	/* mx_attr_t */
		err = mx_iocgattr(q, mp, dp);
		break;
	case _IOC_NR(MX_IOCCMGMT):	/* mx_mgmt_t */
		err = mx_mgmt_size((mx_mgmt_t *) dp->b_rptr);
		break;
	case _IOC_NR(MX_IOCCPASS):	/* mx_pass_t */
		err = mx_pass_size((mx_pass_t *) dp->b_rptr);
		break;
	default:
		err = -EINVAL;
	}
	return (err);
}

noinline fastcall __unlikely int
mx_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(MX_IOCGINFO):	/* mx_info_t */
		err = sizeof(mx_info_t);
		break;
	case _IOC_NR(MX_IOCGOPTION):	/* mx_option_t */
	case _IOC_NR(MX_IOCSOPTION):	/* mx_option_t */
		err = sizeof(mx_option_t);
		break;
	case _IOC_NR(MX_IOCLCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCGCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCSCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCTCONFIG):	/* mx_config_t */
	case _IOC_NR(MX_IOCCCONFIG):	/* mx_config_t */
		err = sizeof(mx_config_t);
		break;
	case _IOC_NR(MX_IOCGSTATEM):	/* mx_statem_t */
	case _IOC_NR(MX_IOCCMRESET):	/* mx_statem_t */
		err = sizeof(mx_statem_t);
		break;
	case _IOC_NR(MX_IOCGSTATUS):	/* mx_status_t */
	case _IOC_NR(MX_IOCSSTATUS):	/* mx_status_t */
	case _IOC_NR(MX_IOCCSTATUS):	/* mx_status_t */
		err = sizeof(mx_status_t);
		break;
	case _IOC_NR(MX_IOCGSTATSP):	/* mx_stats_t */
	case _IOC_NR(MX_IOCSSTATSP):	/* mx_stats_t */
	case _IOC_NR(MX_IOCGSTATS):	/* mx_stats_t */
	case _IOC_NR(MX_IOCCSTATS):	/* mx_stats_t */
		err = sizeof(mx_stats_t);
		break;
	case _IOC_NR(MX_IOCGNOTIFY):	/* mx_notify_t */
	case _IOC_NR(MX_IOCSNOTIFY):	/* mx_notify_t */
	case _IOC_NR(MX_IOCCNOTIFY):	/* mx_notify_t */
		err = sizeof(mx_notify_t);
		break;
	case _IOC_NR(MX_IOCGATTR):	/* mx_attr_t */
		err = sizeof(mx_attr_t);
		break;
	case _IOC_NR(MX_IOCCMGMT):	/* mx_mgmt_t */
		err = sizeof(mx_mgmt_t);
		break;
	case _IOC_NR(MX_IOCCPASS):	/* mx_pass_t */
		err = sizeof(mx_pass_t);
		break;
	default:
		err = -EINVAL;
	}
	return (err);
}

noinline fastcall __unlikely int
sl_copyin2(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	/* SL has no copyin2 stage. */
	__swerr();
	return (-EPROTO);
}

noinline fastcall __unlikely int
sl_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	struct ch *ch;
	int err;

	if ((ch = xp->ch) == NULL)
		return (-ENXIO);
	spin_lock_bh(&ch->lock);
	{
		switch (_IOC_NR(cp->cp_cmd)) {
		case _IOC_NR(SL_IOCSOPTIONS):	/* lmi_option_t */
			err = sl_iocsoptions(ch, dp);
			break;
		case _IOC_NR(SL_IOCSCONFIG):	/* sl_config_t */
			err = sl_iocsconfig(ch, dp);
			break;
		case _IOC_NR(SL_IOCTCONFIG):	/* sl_config_t */
			err = sl_ioctconfig(ch, dp);
			break;
		case _IOC_NR(SL_IOCCCONFIG):	/* sl_config_t */
			err = sl_ioccconfig(ch, dp);
			break;
		case _IOC_NR(SL_IOCCMRESET):	/* sl_statem_t */
			err = sl_ioccmreset(ch, dp);
			break;
		case _IOC_NR(SL_IOCSSTATSP):	/* lmi_sta_t */
			err = sl_iocsstatsp(ch, dp);
			break;
		case _IOC_NR(SL_IOCCSTATS):	/* sl_stats_t */
			err = sl_ioccstats(ch, dp);
			break;
		case _IOC_NR(SL_IOCSNOTIFY):	/* sl_notify_t */
			err = sl_iocsnotify(ch, dp);
			break;
		case _IOC_NR(SL_IOCCNOTIFY):	/* sl_notify_t */
			err = sl_ioccnotify(ch, dp);
			break;
		default:
			__swerr();
			err = -EPROTO;
			break;
		}
	}
	spin_unlock_bh(&ch->lock);
	return (err);
}

noinline fastcall __unlikely int
sl_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(SL_IOCGOPTIONS):	/* lmi_option_t */
		err = (int)-sizeof(lmi_option_t);
		break;
	case _IOC_NR(SL_IOCSOPTIONS):	/* lmi_option_t */
		err = (int)sizeof(lmi_option_t);
		break;
	case _IOC_NR(SL_IOCGCONFIG):	/* sl_config_t */
		err = (int)-sizeof(sl_config_t);
		break;
	case _IOC_NR(SL_IOCSCONFIG):	/* sl_config_t */
	case _IOC_NR(SL_IOCTCONFIG):	/* sl_config_t */
	case _IOC_NR(SL_IOCCCONFIG):	/* sl_config_t */
		err = (int)sizeof(sl_config_t);
		break;
	case _IOC_NR(SL_IOCGSTATEM):	/* sl_statem_t */
		err = (int)-sizeof(sl_statem_t);
		break;
	case _IOC_NR(SL_IOCCMRESET):	/* sl_statem_t */
		err = (int)sizeof(sl_statem_t);
		break;
	case _IOC_NR(SL_IOCGSTATSP):	/* sl_stats_t */
	case _IOC_NR(SL_IOCGSTATS):	/* sl_stats_t */
		err = (int)-sizeof(sl_stats_t);
		break;
	case _IOC_NR(SL_IOCSSTATSP):	/* sl_stats_t */
	case _IOC_NR(SL_IOCCSTATS):	/* sl_stats_t */
		err = (int)sizeof(sl_stats_t);
		break;
	case _IOC_NR(SL_IOCGNOTIFY):	/* sl_notify_t */
		err = (int)-sizeof(sl_notify_t);
		break;
	case _IOC_NR(SL_IOCSNOTIFY):	/* sl_notify_t */
	case _IOC_NR(SL_IOCCNOTIFY):	/* sl_notify_t */
		err = (int)sizeof(sl_notify_t);
		break;
	default:
		goto einval;
	}
	if (err <= 0) {
		mblk_t *db;
		struct ch *ch;

		if ((ch = xp->ch) == NULL)
			goto enxio;
		if (-err > 0 && !(db = mi_copyout_alloc(q, mp, 0, -err, 0)))
			goto enosr;
		err = 0;
		spin_lock_bh(&ch->lock);
		{
			switch (_IOC_NR(ioc->ioc_cmd)) {
			case _IOC_NR(SL_IOCGOPTIONS):	/* lmi_option_t */
				sl_iocgoptions(ch, db);
				break;
			case _IOC_NR(SL_IOCGCONFIG):	/* sl_config_t */
				sl_iocgconfig(ch, db);
				break;
			case _IOC_NR(SL_IOCGSTATEM):	/* sl_statem_t */
				sl_iocgstatem(ch, db);
				break;
			case _IOC_NR(SL_IOCGSTATSP):	/* sl_stats_t */
				sl_iocgstatsp(ch, db);
				break;
			case _IOC_NR(SL_IOCGSTATS):	/* sl_stats_t */
				sl_iocgstats(ch, db);
				break;
			case _IOC_NR(SL_IOCGNOTIFY):	/* sl_notify_t */
				sl_iocgnotify(ch, db);
				break;
			default:
				err = -EFAULT;
				break;
			}
		}
		spin_unlock_bh(&ch->lock);
	}
	return (err);
      enxio:
	return (-ENXIO);
      einval:
	return (-EINVAL);
      enosr:
	return (-ENOSR);
}

noinline fastcall __unlikely int
sdt_copyin2(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	/* SDT has no copyin2 stage. */
	__swerr();
	return (-EPROTO);
}

noinline fastcall __unlikely int
sdt_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	struct ch *ch;
	int err;

	if ((ch = xp->ch) == NULL)
		return (-ENXIO);
	spin_lock_bh(&ch->lock);
	{
		switch (_IOC_NR(cp->cp_cmd)) {
		case _IOC_NR(SDT_IOCSOPTIONS):	/* lmi_option_t */
			err = sdt_iocsoptions(ch, dp);
			break;
		case _IOC_NR(SDT_IOCSCONFIG):	/* sdt_config_t */
			err = sdt_iocsconfig(ch, dp);
			break;
		case _IOC_NR(SDT_IOCTCONFIG):	/* sdt_config_t */
			err = sdt_ioctconfig(ch, dp);
			break;
		case _IOC_NR(SDT_IOCCCONFIG):	/* sdt_config_t */
			err = sdt_ioccconfig(ch, dp);
			break;
		case _IOC_NR(SDT_IOCCMRESET):	/* sdt_statem_t */
			err = sdt_ioccmreset(ch, dp);
			break;
		case _IOC_NR(SDT_IOCSSTATSP):	/* lmi_sta_t */
			err = sdt_iocsstatsp(ch, dp);
			break;
		case _IOC_NR(SDT_IOCCSTATS):	/* sdt_stats_t */
			err = sdt_ioccstats(ch, dp);
			break;
		case _IOC_NR(SDT_IOCSNOTIFY):	/* sdt_notify_t */
			err = sdt_iocsnotify(ch, dp);
			break;
		case _IOC_NR(SDT_IOCCNOTIFY):	/* sdt_notify_t */
			err = sdt_ioccnotify(ch, dp);
			break;
		default:
			__swerr();
			err = -EPROTO;
			break;
		}
	}
	spin_unlock_bh(&ch->lock);
	return (err);
}

noinline fastcall __unlikely int
sdt_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(SDT_IOCGOPTIONS):	/* lmi_option_t */
		err = (int)-sizeof(lmi_option_t);
		break;
	case _IOC_NR(SDT_IOCSOPTIONS):	/* lmi_option_t */
		err = (int)sizeof(lmi_option_t);
		break;
	case _IOC_NR(SDT_IOCGCONFIG):	/* sdt_config_t */
		err = (int)-sizeof(sdt_config_t);
		break;
	case _IOC_NR(SDT_IOCSCONFIG):	/* sdt_config_t */
	case _IOC_NR(SDT_IOCTCONFIG):	/* sdt_config_t */
	case _IOC_NR(SDT_IOCCCONFIG):	/* sdt_config_t */
		err = (int)sizeof(sdt_config_t);
		break;
	case _IOC_NR(SDT_IOCGSTATEM):	/* sdt_statem_t */
		err = (int)-sizeof(sdt_statem_t);
		break;
	case _IOC_NR(SDT_IOCCMRESET):	/* sdt_statem_t */
		err = (int)sizeof(sdt_statem_t);
		break;
	case _IOC_NR(SDT_IOCGSTATSP):	/* lmi_sta_t */
		err = (int)-sizeof(sdt_stats_t);
		break;
	case _IOC_NR(SDT_IOCSSTATSP):	/* lmi_sta_t */
		err = (int)sizeof(lmi_sta_t);
		break;
	case _IOC_NR(SDT_IOCGSTATS):	/* sdt_stats_t */
		err = (int)-sizeof(sdt_stats_t);
		break;
	case _IOC_NR(SDT_IOCCSTATS):	/* sdt_stats_t */
		err = (int)sizeof(sdt_stats_t);
		break;
	case _IOC_NR(SDT_IOCGNOTIFY):	/* sdt_notify_t */
		err = (int)-sizeof(sdt_notify_t);
		break;
	case _IOC_NR(SDT_IOCSNOTIFY):	/* sdt_notify_t */
	case _IOC_NR(SDT_IOCCNOTIFY):	/* sdt_notify_t */
		err = (int)sizeof(sdt_notify_t);
		break;
	case _IOC_NR(SDT_IOCCABORT):	/* */
		err = 0;
		break;
	default:
		goto einval;
	}
	if (err <= 0) {
		mblk_t *db;
		struct ch *ch;

		if ((ch = xp->ch) == NULL)
			goto enxio;
		if (err == 0)
			db = NULL;
		else if (!(db = mi_copyout_alloc(q, mp, 0, -err, 0)))
			goto enosr;
		err = 0;
		spin_lock_bh(&ch->lock);
		{
			switch (_IOC_NR(ioc->ioc_cmd)) {
			case _IOC_NR(SDT_IOCGOPTIONS):	/* lmi_option_t */
				sdt_iocgoptions(ch, db);
				break;
			case _IOC_NR(SDT_IOCGCONFIG):	/* sdt_config_t */
				sdt_iocgconfig(ch, db);
				break;
			case _IOC_NR(SDT_IOCGSTATEM):	/* sdt_statem_t */
				sdt_iocgstatem(ch, db);
				break;
			case _IOC_NR(SDT_IOCGSTATSP):	/* lmi_sta_t */
				sdt_iocgstatsp(ch, db);
				break;
			case _IOC_NR(SDT_IOCGSTATS):	/* sdt_stats_t */
				sdt_iocgstats(ch, db);
				break;
			case _IOC_NR(SDT_IOCGNOTIFY):	/* sdt_notify_t */
				sdt_iocgnotify(ch, db);
				break;
			case _IOC_NR(SDT_IOCCABORT):	/* */
				sdt_ioccabort(ch);
				break;
			default:
				__swerr();
				err = -EFAULT;
				break;
			}
		}
		spin_unlock_bh(&ch->lock);
	}
	return (err);
      enxio:
	return (-ENXIO);
      einval:
	return (-EINVAL);
      enosr:
	return (-ENOSR);
}

noinline fastcall __unlikely int
sdl_copyin2(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	/* SDL has no copyin2 stage. */
	__swerr();
	return (-EPROTO);
}

noinline fastcall __unlikely int
sdl_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	struct ch *ch;
	int err;

	if ((ch = xp->ch) == NULL)
		return (-ENXIO);

	spin_lock_bh(&ch->lock);
	{

		switch (_IOC_NR(cp->cp_cmd)) {
		case _IOC_NR(SDL_IOCSOPTIONS):	/* lmi_option_t */
			err = sdl_iocsoptions(ch, dp);
			break;
		case _IOC_NR(SDL_IOCSCONFIG):	/* sdl_config_t */
			err = sdl_iocsconfig(ch, dp);
			break;
		case _IOC_NR(SDL_IOCTCONFIG):	/* sdl_config_t */
			err = sdl_ioctconfig(ch, dp);
			break;
		case _IOC_NR(SDL_IOCCCONFIG):	/* sdl_config_t */
			err = sdl_ioccconfig(ch, dp);
			break;
		case _IOC_NR(SDL_IOCCMRESET):	/* sdl_statem_t */
			err = sdl_ioccmreset(ch, dp);
			break;
		case _IOC_NR(SDL_IOCSSTATSP):	/* sdl_stats_t */
			err = sdl_iocsstatsp(ch, dp);
			break;
		case _IOC_NR(SDL_IOCCSTATS):	/* sdl_stats_t */
			err = sdl_ioccstats(ch, dp);
			break;
		case _IOC_NR(SDL_IOCSNOTIFY):	/* sdl_notify_t */
			err = sdl_iocsnotify(ch, dp);
			break;
		case _IOC_NR(SDL_IOCCNOTIFY):	/* sdl_notify_t */
			err = sdl_ioccnotify(ch, dp);
			break;
		default:
			__swerr();
			err = -EPROTO;
			break;
		}
	}
	spin_unlock_bh(&ch->lock);
	return (err);
}

noinline fastcall __unlikely int
sdl_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(SDL_IOCGOPTIONS):	/* lmi_option_t */
		err = (int)-sizeof(lmi_option_t);
		break;
	case _IOC_NR(SDL_IOCSOPTIONS):	/* lmi_option_t */
		err = (int)sizeof(lmi_option_t);
		break;
	case _IOC_NR(SDL_IOCGCONFIG):	/* sdl_config_t */
		err = (int)-sizeof(sdl_config_t);
		break;
	case _IOC_NR(SDL_IOCSCONFIG):	/* sdl_config_t */
	case _IOC_NR(SDL_IOCTCONFIG):	/* sdl_config_t */
	case _IOC_NR(SDL_IOCCCONFIG):	/* sdl_config_t */
		err = (int)sizeof(sdl_config_t);
		break;
	case _IOC_NR(SDL_IOCGSTATEM):	/* sdl_statem_t */
		err = (int)-sizeof(sdl_statem_t);
		break;
	case _IOC_NR(SDL_IOCCMRESET):	/* sdl_statem_t */
		err = (int)sizeof(sdl_statem_t);
		break;
	case _IOC_NR(SDL_IOCGSTATSP):	/* sdl_stats_t */
		err = (int)-sizeof(sdl_stats_t);
		break;
	case _IOC_NR(SDL_IOCGSTATS):	/* sdl_stats_t */
		err = (int)-sizeof(sdl_stats_t);
		break;
	case _IOC_NR(SDL_IOCSSTATSP):	/* sdl_stats_t */
	case _IOC_NR(SDL_IOCCSTATS):	/* sdl_stats_t */
		err = (int)sizeof(sdl_stats_t);
		break;
	case _IOC_NR(SDL_IOCGNOTIFY):	/* sdl_notify_t */
		err = (int)-sizeof(sdl_notify_t);
		break;
	case _IOC_NR(SDL_IOCSNOTIFY):	/* sdl_notify_t */
	case _IOC_NR(SDL_IOCCNOTIFY):	/* sdl_notify_t */
		err = (int)sizeof(sdl_notify_t);
		break;
	case _IOC_NR(SDL_IOCCDISCTX):	/* */
		err = 0;
		break;
	case _IOC_NR(SDL_IOCCCONNTX):	/* */
		err = 0;
		break;
	default:
		goto einval;
	}
	if (err <= 0) {
		mblk_t *db;
		struct ch *ch;

		if ((ch = xp->ch) == NULL)
			goto enxio;
		if (err == 0)
			db = NULL;
		else if (!(db = mi_copyout_alloc(q, mp, 0, -err, 0)))
			goto enosr;
		err = 0;
		spin_lock_bh(&ch->lock);
		{
			switch (_IOC_NR(ioc->ioc_cmd)) {
			case _IOC_NR(SDL_IOCGOPTIONS):	/* lmi_option_t */
				sdl_iocgoptions(ch, db);
				break;
			case _IOC_NR(SDL_IOCGCONFIG):	/* sdl_config_t */
				sdl_iocgconfig(ch, db);
				break;
			case _IOC_NR(SDL_IOCGSTATEM):	/* sdl_statem_t */
				sdl_iocgstatem(ch, db);
				break;
			case _IOC_NR(SDL_IOCGSTATSP):	/* sdl_stats_t */
				sdl_iocgstatsp(ch, db);
				break;
			case _IOC_NR(SDL_IOCGSTATS):	/* sdl_stats_t */
				sdl_iocgstats(ch, db);
				break;
			case _IOC_NR(SDL_IOCGNOTIFY):	/* sdl_notify_t */
				sdl_iocgnotify(ch, db);
				break;
			case _IOC_NR(SDL_IOCCDISCTX):	/* */
				sdl_ioccdisctx(ch);
				break;
			case _IOC_NR(SDL_IOCCCONNTX):	/* */
				sdl_ioccconntx(ch);
				break;
			default:
				__swerr();
				err = -EFAULT;
				break;
			}
		}
		spin_unlock_bh(&ch->lock);
	}
	return (err);
      enxio:
	return (-ENXIO);
      einval:
	return (-EINVAL);
      enosr:
	return (-ENOSR);
}

noinline fastcall __unlikely int
nit_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
	case _IOC_NR(NIOCBIND):
		err = nit_niocbind(xp, dp);
		break;
	case _IOC_NR(NIOCSSNAP):
#ifdef __LP64__
		if (cp->cp_flag == IOC_ILP32)
			err = nit_niocssnap32(xp, dp);
		else
#endif				/* __LP64__ */
			err = nit_niocssnap(xp, dp);
		break;
	case _IOC_NR(NIOCGSNAP):
		__swerr();
		err = -EPROTO;
		break;
	case _IOC_NR(NIOCSFLAGS):
#ifdef __LP64__
		if (cp->cp_flag == IOC_ILP32)
			err = nit_niocsflags32(xp, dp);
		else
#endif				/* __LP64__ */
			err = nit_niocsflags(xp, dp);
		break;
	case _IOC_NR(NIOCGFLAGS):
		__swerr();
		err = -EPROTO;
		break;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
nit_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(NIOCBIND):
		err = sizeof(struct ifreq);
		break;
	case _IOC_NR(NIOCSSNAP):	/* ulong */
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = sizeof(uint32_t);
		else
#endif				/* __LP64__ */
			err = sizeof(ulong);
		break;
	case _IOC_NR(NIOCGSNAP):	/* ulong */
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = nit_niocgsnap32(q, mp, xp);
		else
#endif				/* __LP64__ */
			err = nit_niocgsnap(q, mp, xp);
		break;
	case _IOC_NR(NIOCSFLAGS):	/* ulong */
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = sizeof(uint32_t);
		else
#endif				/* __LP64__ */
			err = sizeof(ulong);
		break;
	case _IOC_NR(NIOCGFLAGS):	/* ulong */
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = nit_niocgflags32(q, mp, xp);
		else
#endif				/* __LP64__ */
			err = nit_niocgflags(q, mp, xp);
		break;
	default:
		err = -EINVAL;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
bpf_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
	case _IOC_NR(BIOCGDLT):	/* uint */
		goto eproto;
	case _IOC_NR(BIOCSDLT):	/* uint */
		err = bpf_biocsdlt(q, mp, xp, dp);
		break;
	case _IOC_NR(BIOCGDLTLIST):	/* bpf_dltlist */
		err = bpf_biocgdltlist(q, mp, xp, cp, dp);
		break;
	case _IOC_NR(BIOCPROMISC):	/* (none) */
		goto eproto;
	case _IOC_NR(BIOCFLUSH):	/* (none) */
		goto eproto;
	case _IOC_NR(BIOCGETIF):	/* ifreq */
		// se _IOC_NR(BIOCGETLIF): /* lifreq */
		switch (cp->cp_cmd) {
		case BIOCGETIF:
			goto eproto;
		case BIOCGETLIF:
			goto eproto;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSETIF):	/* ifreq */
		// se _IOC_NR(BIOCSETLIF): /* lifreq */
		switch (cp->cp_cmd) {
		case BIOCSETIF:
			err = bpf_biocsetif(q, xp, dp);
			break;
		case BIOCSETLIF:
			err = bpf_biocsetlif(q, xp, dp);
			break;
		}
		goto eproto;
	case _IOC_NR(BIOCGSTATS):	/* bpf_stat or bpf_stat_old */
		switch (cp->cp_cmd) {
		case BIOCGSTATS:
			goto eproto;
		case BIOCGSTATSOLD:
			goto eproto;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCGHDRCMPLT):	/* uint */
		goto eproto;
	case _IOC_NR(BIOCSHDRCMPLT):	/* uint */
		err = bpf_biocshdrcmplt(xp, dp);
		break;
	case _IOC_NR(BIOCGSEESENT):	/* uint */
		goto eproto;
	case _IOC_NR(BIOCSSEESENT):	/* uint */
		err = bpf_biocsseesent(xp, dp);
		break;
#ifdef BIOCGDIRECTION
	case _IOC_NR(BIOCGDIRECTION):	/* uint */
		goto eproto;
#endif
#ifdef BIOCSDIRECTION
	case _IOC_NR(BIOCSDIRECTION):	/* uint */
		err = bpf_biocsdirection(xp, dp);
		break;
#endif
#ifdef BIOCGDIRFILT
	case _IOC_NR(BIOCGDIRFILT):	/* uint */
		goto eproto;
#endif				/* BIOCGDIRFILT */
#ifdef BIOCSDIRFILT
	case _IOC_NR(BIOCSDIRFILT):	/* uint */
		err = bpf_biocsdirfilt(xp, dp);
		break;
#endif				/* BIOCSDIRFILT */
#ifdef BIOCFEEDBACK
	case _IOC_NR(BIOCFEEDBACK):	/* uint */
#endif
	case _IOC_NR(BIOCSFEEDBACK):	/* uint */
		err = bpf_biocsfeedback(xp, dp);
		break;
	case _IOC_NR(BIOCGFEEDBACK):	/* uint */
		goto eproto;

/* -------------------------------------------------------------------------- */
/* Remaining input-output controls must be processed by the module bpfmod(4). */
/* -------------------------------------------------------------------------- */

	case _IOC_NR(BIOCGBLEN):
		// or _IOC_NR(BIOCSBLEN):
		switch (cp->cp_cmd) {
		case BIOCSBLEN:
			goto eio;
		case BIOCGBLEN:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCGRTIMEOUT):
		goto eio;
	case _IOC_NR(BIOCSRTIMEOUT):
		// or _IOC_NR(BIOCLOCK):
		switch (cp->cp_cmd) {
		case BIOCSRTIMEOUT:
			goto eio;
		case BIOCLOCK:
			goto eio;
		}
		goto eopnotsupp;
#ifdef BIOCGFILDROP
	case _IOC_NR(BIOCGFILDROP):
		goto eio;
#endif				/* BIOCGFILDROP */
#ifdef BIOCSFILDROP
	case _IOC_NR(BIOCSFILDROP):
		goto eio;
#endif				/* BIOCSFILDROP */
	case _IOC_NR(BIOCIMMEDIATE):
		goto eio;
	case _IOC_NR(BIOCSETF):
		goto eio;
	case _IOC_NR(BIOCSETFNR):
		goto eio;
#ifdef BIOCSETWF
	case _IOC_NR(BIOCSETWF):
		goto eio;
#endif				/* BIOCSETWF */
	case _IOC_NR(BIOCVERSION):
		goto eio;
	case _IOC_NR(BIOCSTCPF):
		// or _IOC_NR(BIOCGRSIG):
		switch (cp->cp_cmd) {
		case BIOCSTCPF:
			goto eio;
		case BIOCGRSIG:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSUDPF):
		// se _IOC_NR(BIOCSRSIG):
		switch (cp->cp_cmd) {
		case BIOCSUDPF:
			goto eio;
		case BIOCSRSIG:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSTSTAMP):
		goto eio;
	case _IOC_NR(BIOCGTSTAMP):
		// or _IOC_NR(BIOCGETBUFMODE):
		switch (cp->cp_cmd) {
		case BIOCGTSTAMP:
			goto eio;
		case BIOCGETBUFMODE:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSETBUFMODE):
		goto eio;
	case _IOC_NR(BIOCSETZBUF):
		goto eio;
	case _IOC_NR(BIOCGETZMAX):
		goto eio;
	case _IOC_NR(BIOCROTZBUF):
		goto eio;
	default:
		goto eopnotsupp;
	}
	return (err);
      eopnotsupp:
	/* This would be simple, but because this is M_IOCDATA that we should have rejected at the M_IOCTL
	   stage, it means that some upper module is leaking M_IOCDATA down to us.  The problem is likely
	   with that module and not in this driver.  Return EPROTO to distinguish. */
	__swerr();
	return (-EPROTO);
      eproto:
	/* Should have completed before this stage.  This is a pretty significant error and indicates that we 
	   are not processing phases for the input-output control correctly. */
	__swerr();
	return (-EPROTO);
      eio:
	/* Should have been processed by module bpfmod(4).  This is a pretty significant error because we
	   should have rejected the input-output control in bpf_ioctl().  So, this probably represents a
	   leakage from and bug in bpfmod(4) rather than here. */
	__swerr();
	return (-EIO);
}

noinline fastcall __unlikely int
bpf_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
	case _IOC_NR(BIOCGDLT):	/* uint */
		err = bpf_biocgdlt(q, mp, xp);
		break;
	case _IOC_NR(BIOCSDLT):	/* uint */
		err = sizeof(uint);
		break;
	case _IOC_NR(BIOCGDLTLIST):	/* bpf_dltlist */
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = sizeof(struct bpf_dltlist32);
		else
#endif				/* __LP64__ */
			err = sizeof(struct bpf_dltlist);
		break;
	case _IOC_NR(BIOCPROMISC):	/* (none) */
		err = bpf_biocpromisc(xp);
		break;
	case _IOC_NR(BIOCFLUSH):	/* (none) */
		err = bpf_biocflush(q, xp);
		break;
	case _IOC_NR(BIOCGETIF):	/* ifreq */
		switch (ioc->ioc_cmd) {
		case BIOCGETIF:
			err = bpf_biocgetif(q, mp, xp);
			break;
		case BIOCGETLIF:
			err = bpf_biocgetlif(q, mp, xp);
			break;
		default:
			goto eopnotsupp;
		}
		break;
	case _IOC_NR(BIOCSETIF):	/* ifreq */
		// se _IOC_NR(BIOCSETLIF): /* lifreq */
		switch (ioc->ioc_cmd) {
		case BIOCGETIF:
			err = sizeof(struct ifreq);
			break;
		case BIOCGETLIF:
			err = sizeof(struct lifreq);
			break;
		default:
			goto eopnotsupp;
		}
	case _IOC_NR(BIOCGSTATS):	/* bpf_stat or bpf_stat_old */
		switch (ioc->ioc_cmd) {
		case BIOCGSTATS:
			err = bpf_biocgstats(q, mp, xp);
			break;
		case BIOCGSTATSOLD:
			err = bpf_biocgstatsold(q, mp, xp);
			break;
		default:
			goto eopnotsupp;
		}
		break;
	case _IOC_NR(BIOCGHDRCMPLT):	/* uint */
		err = bpf_biocghdrcmplt(q, mp, xp);
		break;
	case _IOC_NR(BIOCSHDRCMPLT):	/* uint */
		err = sizeof(uint);
		break;
	case _IOC_NR(BIOCGSEESENT):	/* uint */
		err = bpf_biocgseesent(q, mp, xp);
		break;
	case _IOC_NR(BIOCSSEESENT):	/* uint */
		err = sizeof(uint);
		break;
#ifdef BIOCGDIRECTION
	case _IOC_NR(BIOCGDIRECTION):	/* uint */
		err = bpf_biocgdirection(q, mp, xp);
		break;
#endif				/* BIOCGDIRECTION */
#ifdef BIOCSDIRECTION
	case _IOC_NR(BIOCSDIRECTION):	/* uint */
		err = sizeof(uint);
		break;
#endif				/* BIOCSDIRECTION */
#ifdef BIOCGDIRFILT
	case _IOC_NR(BIOCGDIRFILT):	/* uint */
		err = bpf_biocgdirfilt(q, mp, xp);
		break;
#endif				/* BIOCGDIRFILT */
#ifdef BIOCSDIRFILT
	case _IOC_NR(BIOCSDIRFILT):	/* uint */
		err = sizeof(uint);
		break;
#endif				/* BIOCSDIRFILT */
#ifdef BIOCFEEDBACK
	case _IOC_NR(BIOCFEEDBACK):	/* uint */
		err = sizeof(uint);
		break;
#endif				/* BIOCFEEDBACK */
	case _IOC_NR(BIOCGFEEDBACK):	/* uint */
		err = bpf_biocgfeedback(q, mp, xp);
		break;
	case _IOC_NR(BIOCSFEEDBACK):	/* uint */
		err = sizeof(uint);
		break;

/* -------------------------------------------------------------------------- */
/* Remaining input-output controls must be processed by the module bpfmod(4). */
/* -------------------------------------------------------------------------- */

	case _IOC_NR(BIOCGBLEN):
		// or _IOC_NR(BIOCSBLEN):
		switch (ioc->ioc_cmd) {
		case BIOCSBLEN:
			goto eio;
		case BIOCGBLEN:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCGRTIMEOUT):
		goto eio;
	case _IOC_NR(BIOCSRTIMEOUT):
		// or _IOC_NR(BIOCLOCK):
		switch (ioc->ioc_cmd) {
		case BIOCSRTIMEOUT:
			goto eio;
		case BIOCLOCK:
			goto eio;
		}
		goto eopnotsupp;
#ifdef BIOCGFILDROP
	case _IOC_NR(BIOCGFILDROP):
		goto eio;
#endif				/* BIOCGFILDROP */
#ifdef BIOCSFILDROP
	case _IOC_NR(BIOCSFILDROP):
		goto eio;
#endif				/* BIOCSFILDROP */
	case _IOC_NR(BIOCIMMEDIATE):
		goto eio;
	case _IOC_NR(BIOCSETF):
		goto eio;
	case _IOC_NR(BIOCSETFNR):
		goto eio;
#ifdef BIOCSETWF
	case _IOC_NR(BIOCSETWF):
		goto eio;
#endif				/* BIOCSETWF */
	case _IOC_NR(BIOCVERSION):
		goto eio;
	case _IOC_NR(BIOCSTCPF):
		// or _IOC_NR(BIOCGRSIG):
		switch (ioc->ioc_cmd) {
		case BIOCSTCPF:
			goto eio;
		case BIOCGRSIG:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSUDPF):
		// se _IOC_NR(BIOCSRSIG):
		switch (ioc->ioc_cmd) {
		case BIOCSUDPF:
			goto eio;
		case BIOCSRSIG:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSTSTAMP):
		goto eio;
	case _IOC_NR(BIOCGTSTAMP):
		// or _IOC_NR(BIOCGETBUFMODE):
		switch (ioc->ioc_cmd) {
		case BIOCGTSTAMP:
			goto eio;
		case BIOCGETBUFMODE:
			goto eio;
		}
		goto eopnotsupp;
	case _IOC_NR(BIOCSETBUFMODE):
		goto eio;
	case _IOC_NR(BIOCSETZBUF):
		goto eio;
	case _IOC_NR(BIOCGETZMAX):
		goto eio;
	case _IOC_NR(BIOCROTZBUF):
		goto eio;
	default:
		goto eopnotsupp;
	}
	return (err);
      eopnotsupp:
	/* This is pretty simple: we were passed an input-output control that we do not recognize. Because
	   BPF is a BSD'ish thing, we return EOPNOTSUPP as BSD does instead of EINVAL as SVR4 does. */
	return (-EOPNOTSUPP);
      eio:
	/* Should have been processed by module bpfmod(4).  This is a pretty significant error because we
	   autopush bpfmod(4) so this probably represents a leakage from and bug in bpfmod(4) rather than
	   here. */
	__swerr();
	return (-EINVAL);
}

noinline fastcall __unlikely int
sio_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
#ifdef SIOCGIFNUM
	case _IOC_NR(SIOCGIFNUM):
		goto eproto;
#endif
	case _IOC_NR(SIOCGIFCONF):
#ifdef __LP64__
		if (cp->cp_flag == IOC_ILP32)
			err = sio_siocgifconf32(q, mp, xp, dp);
		else
#endif				/* __LP64__ */
			err = sio_siocgifconf(q, mp, xp, dp);
		break;
	case _IOC_NR(SIOCGIFFLAGS):
		err = sio_siocgifflags(q, mp, xp, dp);
		break;
	case _IOC_NR(SIOCSIFFLAGS):
		err = sio_siocsifflags(q, mp, xp, dp);
		break;
	case _IOC_NR(SIOCGIFINDEX):
		err = sio_siocgifindex(q, mp, xp, dp);
		break;
#ifdef SIOCSIFINDEX
	case _IOC_NR(SIOCSIFINDEX):
		err = sio_siocsifindex(q, mp, xp, dp);
		break;
#endif
	case _IOC_NR(SIOCGIFHWADDR):
		err = sio_siocgifhwaddr(q, mp, xp, dp);
		break;
	case _IOC_NR(SIOCSIFHWADDR):
		err = sio_siocsifhwaddr(q, mp, xp, dp);
		break;
#ifdef SIOCGLIFNUM
	case _IOC_NR(SIOCGLIFNUM):
		err = sio_siocglifnum(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCGLIFCONF
	case _IOC_NR(SIOCGLIFCONF):
#ifdef __LP64__
		if (cp->cp_flag == IOC_ILP32)
			err = sio_siocglifconf32(q, mp, xp, dp);
		else
#endif				/* __LP64__ */
			err = sio_siocglifconf(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCSLIFNAME
	case _IOC_NR(SIOCSLIFNAME):
		err = sio_siocslifname(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCGLIFFLAGS
	case _IOC_NR(SIOCGLIFFLAGS):
		err = sio_siocglifflags(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCSLIFFLAGS
	case _IOC_NR(SIOCSLIFFLAGS):
		err = sio_siocslifflags(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCGLIFINDEX
	case _IOC_NR(SIOCGLIFINDEX):
		err = sio_siocglifindex(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCSLIFINDEX
	case _IOC_NR(SIOCSLIFINDEX):
		err = sio_siocslifindex(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCGLIFHWADDR
	case _IOC_NR(SIOCGLIFHWADDR):
		err = sio_siocglifhwaddr(q, mp, xp, dp);
		break;
#endif
#ifdef SIOCSLIFHWADDR
	case _IOC_NR(SIOCSLIFHWADDR):
		err = sio_siocslifhwaddr(q, mp, xp, dp);
		break;
#endif

/* ---------------------------------------------------------- */
/* All of the remaining use addresses that are not supported. */
/* ---------------------------------------------------------- */

	case _IOC_NR(SIOCGIFADDR):
	case _IOC_NR(SIOCSIFADDR):
	case _IOC_NR(SIOCGIFNETMASK):
	case _IOC_NR(SIOCSIFNETMASK):
	case _IOC_NR(SIOCGIFBRDADDR):
	case _IOC_NR(SIOCSIFBRDADDR):
	case _IOC_NR(SIOCGIFDSTADDR):
	case _IOC_NR(SIOCSIFDSTADDR):
#ifdef SIOCGLIFADDR
	case _IOC_NR(SIOCGLIFADDR):
#endif
#ifdef SIOCSLIFADDR
	case _IOC_NR(SIOCSLIFADDR):
#endif
#ifdef SIOCGLIFNETMASK
	case _IOC_NR(SIOCGLIFNETMASK):
#endif
#ifdef SIOCSLIFNETMASK
	case _IOC_NR(SIOCSLIFNETMASK):
#endif
#ifdef SIOCGLIFBRDADDR
	case _IOC_NR(SIOCGLIFBRDADDR):
#endif
#ifdef SIOCSLIFBRDADDR
	case _IOC_NR(SIOCSLIFBRDADDR):
#endif
#ifdef SIOCGLIFDSTADDR
	case _IOC_NR(SIOCGLIFDSTADDR):
#endif
#ifdef SIOCSLIFDSTADDR
	case _IOC_NR(SIOCSLIFDSTADDR):
#endif
		goto eproto;
	default:
		goto eopnotsupp;
	}
	return (err);
      eproto:
	__swerr();
	return (-EPROTO);
      eopnotsupp:
	__swerr();
	return (-EPROTO);
}

noinline fastcall __unlikely int
sio_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
#ifdef SIOCGIFNUM
	case _IOC_NR(SIOCGIFNUM):
		err = sio_siocgifnum(q, mp, xp);
		break;
#endif
	case _IOC_NR(SIOCGIFCONF):
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = sizeof(struct ifconf32);
		else
#endif				/* __LP64__ */
			err = sizeof(struct ifconf);
		break;
	case _IOC_NR(SIOCGIFFLAGS):
		err = sizeof(struct ifreq);
		break;
	case _IOC_NR(SIOCSIFFLAGS):
		/* Note that SIOCSIFFLAGS also sets the PPA. */
		err = sizeof(struct ifreq);
		break;
#ifdef SIOCGLIFNUM
	case _IOC_NR(SIOCGLIFNUM):
		err = sizeof(struct lifnum);
		break;
#endif
#ifdef SIOCGLIFCONF
	case _IOC_NR(SIOCGLIFCONF):
#ifdef __LP64__
		if (ioc->ioc_flag == IOC_ILP32)
			err = sizeof(struct lifconf32);
		else
#endif				/* __LP64__ */
			err = sizeof(struct lifconf);
		break;
#endif
#ifdef SIOCSLIFNAME
	case _IOC_NR(SIOCSLIFNAME):
		/* Note that SIOCSLIFNAME also sets the PPA */
		err = sizeof(struct lifreq);
		break;
#endif
#ifdef SIOCGLIFFLAGS
	case _IOC_NR(SIOCGLIFFLAGS):
		err = sizeof(struct lifreq);
		break;
#endif
#ifdef SIOCSLIFFLAGS
	case _IOC_NR(SIOCSLIFFLAGS):
		/* Note that SIOCSLIFFLAGS could also be used to set PPA. */
		err = sizeof(struct lifreq);
		break;
#endif
#ifdef SIOCGLIFINDEX
	case _IOC_NR(SIOCGLIFINDEX):
		err = sizeof(struct lifreq);
		break;
#endif
#ifdef SIOCSLIFINDEX
	case _IOC_NR(SIOCSLIFINDEX):
		err = sizeof(struct lifreq);
		break;
#endif
#ifdef SIOCGLIFHWADDR
	case _IOC_NR(SIOCGLIFHWADDR):
		err = sizeof(struct lifreq);
		break;
#endif
#ifdef SIOCSLIFHWADDR
	case _IOC_NR(SIOCSLIFHWADDR):
		err = sizeof(struct lifreq);
		break;
#endif

/* ---------------------------------------------------------- */
/* All of the remaining use addresses that are not supported. */
/* ---------------------------------------------------------- */

	case _IOC_NR(SIOCGIFADDR):
	case _IOC_NR(SIOCSIFADDR):
	case _IOC_NR(SIOCGIFNETMASK):
	case _IOC_NR(SIOCSIFNETMASK):
	case _IOC_NR(SIOCGIFBRDADDR):
	case _IOC_NR(SIOCSIFBRDADDR):
	case _IOC_NR(SIOCGIFDSTADDR):
	case _IOC_NR(SIOCSIFDSTADDR):
#ifdef SIOCGLIFADDR
	case _IOC_NR(SIOCGLIFADDR):
#endif
#ifdef SIOCSLIFADDR
	case _IOC_NR(SIOCSLIFADDR):
#endif
#ifdef SIOCGLIFNETMASK
	case _IOC_NR(SIOCGLIFNETMASK):
#endif
#ifdef SIOCSLIFNETMASK
	case _IOC_NR(SIOCSLIFNETMASK):
#endif
#ifdef SIOCGLIFBRDADDR
	case _IOC_NR(SIOCGLIFBRDADDR):
#endif
#ifdef SIOCSLIFBRDADDR
	case _IOC_NR(SIOCSLIFBRDADDR):
#endif
#ifdef SIOCGLIFDSTADDR
	case _IOC_NR(SIOCGLIFDSTADDR):
#endif
#ifdef SIOCSLIFDSTADDR
	case _IOC_NR(SIOCSLIFDSTADDR):
#endif
		err = -EADDRNOTAVAIL;
		break;
	default:
		err = -EINVAL;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
dl_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_NR(cp->cp_cmd)) {
#ifdef DLIOCRAW
	case _IOC_NR(DLIOCRAW):	/* */
		goto eproto;
#endif				/* DLIOCRAW */
#ifdef DLIOCNATIVE
	case _IOC_NR(DLIOCNATIVE):	/* *//* new dl_mac_type in return value */
		goto eproto;
#endif				/* DLIOCNATIVE */
#ifdef DLIOCMARGININFO
	case _IOC_NR(DLIOCMARGININFO):	/* int */
		goto eproto;
#endif				/* DLIOCMARGININFO */
#ifdef DLIOCHDRINFO
	case _IOC_NR(DLIOCHDRINFO):
		if (cp->cp_flag != IOC_NONE)
			goto eacces;
		err = dl_dliochdrinfo(xp, dp);
		break;
#endif				/* DLIOCHDRINFO */
#ifdef DL_IOC_DRIVER_OPTIONS
	case _IOC_NR(DL_IOC_DRIVER_OPTIONS):	/* driver_ops_t */
		if (cp->cp_flag != IOC_NONE)
			goto eacces;
		err = dl_ioc_driver_options(xp, dp);
		break;
#endif				/* DL_IOC_DRIVER_OPTIONS */
#ifndef DLIOCHDRINFO
#ifdef DL_IOC_HDR_INFO
	case _IOC_NR(DL_IOC_HDR_INFO):
		if (cp->cp_flag != IOC_NONE)
			goto eacces;
		err = dl_ioc_hdr_info(xp, dp);
		break;
#endif				/* DL_IOC_HDR_INFO */
#endif				/* DLIOCHDRINFO */
#ifdef DL_HP_SET_DRV_PARAM_IOCTL
	case _IOC_NR(DL_HP_SET_DRV_PARAM_IOCTL):	/* dl_hp_set_drv_param_ioctl_t */
		err = dl_hp_set_drv_param_ioctl(xp, dp);
		break;
#endif				/* DL_HP_SET_DRV_PARAM_IOCTL */
#ifdef DL_HP_GET_DRV_PARAM_IOCTL
	case _IOC_NR(DL_HP_GET_DRV_PARAM_IOCTL):	/* dl_hp_set_drv_param_ioctl_t */
		goto eproto;
#endif				/* DL_HP_GET_DRV_PARAM_IOCTL */
#ifdef DLPI_SET_NO_LOOPBACK
	case _IOC_NR(DLPI_SET_NO_LOOPBACK):	/* uint32_t */
		err = dl_set_no_loopback(xp, dp);
		break;
#endif				/* DLPI_SET_NO_LOOPBACK */
	default:
		goto eproto;
	}
	return (err);
      eproto:
	__swerr();
	return (-EPROTO);
      eacces:
	return (-EACCES);
}

noinline fastcall __unlikely int
dl_ioctl(queue_t *q, mblk_t *mp, struct xp *xp, struct iocblk *ioc)
{
	int err;

	switch (_IOC_NR(ioc->ioc_cmd)) {
#ifdef DLIOCRAW
	case _IOC_NR(DLIOCRAW):	/* */
		err = dl_dliocraw(q, mp, xp);
		break;
#endif				/* DLIOCRAW */
#ifdef DLIOCNATIVE
	case _IOC_NR(DLIOCNATIVE):	/* *//* new dl_mac_type in return value */
		err = dl_dliocnative(q, mp, xp);
		break;
#endif				/* DLIOCNATIVE */
#ifdef DLIOCMARGININFO
	case _IOC_NR(DLIOCMARGININFO):	/* int */
		err = dl_dliocmargininfo(q, mp, xp);
		break;
#endif				/* DLIOCMARGININFO */
#ifdef DLIOCHDRINFO
	case _IOC_NR(DLIOCHDRINFO):	/* dl_unitdata_req_t */
		/* Solaris doesn't let user space programs issue this. */
		if (ioc->ioc_flag != IOC_NONE)
			goto eacces;
		err = sizeof(dl_unitdata_req_t);
		break;
#endif				/* DLIOCHDRINFO */
#ifdef DL_IOC_DRIVER_OPTIONS
	case _IOC_NR(DL_IOC_DRIVER_OPTIONS):	/* driver_ops_t */
		/* HP doesn't let user space programs issue this. */
		if (ioc->ioc_flag != IOC_NONE)
			goto eacces;
		err = sizeof(driver_ops_t);
		break;
#endif				/* DL_IOC_DRIVER_OPTIONS */
#ifndef DLIOCHDRINFO
#ifdef DL_IOC_HDR_INFO
	case _IOC_NR(DL_IOC_HDR_INFO):	/* dl_unitdata_req_t */
		/* HP doesn't let user space programs issue this. */
		if (ioc->ioc_flag != IOC_NONE)
			goto eacces;
		err = sizeof(dl_unitdata_req_t);
		break;
#endif				/* DL_IOC_HDR_INFO */
#endif				/* DLIOCHDRINFO */
#ifdef DL_HP_SET_DRV_PARAM_IOCTL
	case _IOC_NR(DL_HP_SET_DRV_PARAM_IOCTL):	/* dl_hp_set_drv_param_ioctl_t */
		err = sizeof(dl_hp_set_drv_param_ioctl_t);
		break;
#endif				/* DL_HP_SET_DRV_PARAM_IOCTL */
#ifdef DL_HP_GET_DRV_PARAM_IOCTL
	case _IOC_NR(DL_HP_GET_DRV_PARAM_IOCTL):
		err = dl_hp_get_drv_param_ioctl(q, mp, xp);
#endif				/* DL_HP_GET_DRV_PARAM_IOCTL */
#ifdef DLPI_SET_NO_LOOPBACK
	case _IOC_NR(DLPI_SET_NO_LOOPBACK):	/* uint32_t */
		err = sizeof(uint32_t);
		break;
#endif				/* DLPI_SET_NO_LOOPBACK */
	default:
		goto einval;
	}
	return (err);
      einval:
	return (-EINVAL);
      eacces:
	return (-EACCES);
}

/** xp_w_copyin2: - second copyin stage
  * @q: the write queue
  * @mp: the M_COPYIN message
  * @xp: private structure pointer
  * @cp: the copyresp structure pointer
  * @dp: the M_COPYIN data block
  *
  * This is the second stage copyin.  The first stage copyin is used to copy in the header of the
  * structure used for set commands and other commands that require an object-specific structure
  * following the header.  The second stage is used to copy in the object-specific structure.
  */
noinline fastcall __unlikely int
xp_w_copyin2(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_TYPE(cp->cp_cmd)) {
	case DSX_IOC_MAGIC:
		err = dsx_copyin2(q, mp, xp, cp, dp);
		break;
	case MX_IOC_MAGIC:
		err = mx_copyin2(q, mp, xp, cp, dp);
		break;
	case SL_IOC_MAGIC:
		err = sl_copyin2(q, mp, xp, cp, dp);
		break;
	case SDT_IOC_MAGIC:
		err = sdt_copyin2(q, mp, xp, cp, dp);
		break;
	case SDL_IOC_MAGIC:
		err = sdl_copyin2(q, mp, xp, cp, dp);
		break;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	return (err);
}

/** xp_w_copyin: - first copyin stage
  * @q: write queue
  * @mp: M_COPYIN message
  * @xp: private structure pointer
  * @cp: copyresp structure pointer
  * @dp: M_COPYIN data block
  *
  * This is the first copyin stage.  Many of the input-output controls require that a structure with
  * the object type and identifier first be copied in, even on get operations.   Usually get
  * operations return their result at this first stage; set operations perform an additional copyin
  * stage and return their result from the second copyin stage.
  */
noinline fastcall __unlikely int
xp_w_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_TYPE(cp->cp_cmd)) {
	case DSX_IOC_MAGIC:
		err = dsx_copyin(q, mp, xp, cp, dp);
		break;
	case MX_IOC_MAGIC:
		err = mx_copyin(q, mp, xp, cp, dp);
		break;
	case SL_IOC_MAGIC:
		err = sl_copyin(q, mp, xp, cp, dp);
		break;
	case SDT_IOC_MAGIC:
		err = sdt_copyin(q, mp, xp, cp, dp);
		break;
	case SDL_IOC_MAGIC:
		err = sdl_copyin(q, mp, xp, cp, dp);
		break;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
xp_w_iocdata(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct copyresp *cp = (typeof(cp)) mp->b_rptr;
	mblk_t *dp;
	int err = 0;

	switch (mi_copy_state(q, mp, &dp)) {
	case MI_COPY_CASE(MI_COPY_IN, 1):
		err = xp_w_copyin(q, mp, xp, cp, dp);
		break;
	case MI_COPY_CASE(MI_COPY_IN, 2):
		err = xp_w_copyin2(q, mp, xp, cp, dp);
		break;
	case MI_COPY_CASE(MI_COPY_OUT, 1):
		__trace();
		break;
	case -1:
		__trace();
		goto efault;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
      efault:
	return (0);
}

noinline fastcall __unlikely int
xp_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct iocblk *ioc = (typeof(ioc)) mp->b_rptr;
	int err = 0;

	switch (_IOC_TYPE(ioc->ioc_cmd)) {
	case DSX_IOC_MAGIC:
		err = dsx_ioctl(q, mp, xp, ioc);
		break;
	case MX_IOC_MAGIC:
		err = mx_ioctl(q, mp, xp, ioc);
		break;
	case SL_IOC_MAGIC:
		err = sl_ioctl(q, mp, xp, ioc);
		break;
	case SDT_IOC_MAGIC:
		err = sdt_ioctl(q, mp, xp, ioc);
		break;
	case SDL_IOC_MAGIC:
		err = sdl_ioctl(q, mp, xp, ioc);
		break;
	default:
		err = -EINVAL;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
	return (0);
}

noinline fastcall __unlikely int
nit_w_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_TYPE(cp->cp_cmd)) {
	case _IOC_TYPE(NIOC):
		err = nit_copyin(q, mp, xp, cp, dp);
		break;
	case _IOC_TYPE(SIOCGIFCONF):
		err = sio_copyin(q, mp, xp, cp, dp);
		break;
	default:
		err = -EINVAL;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
nit_w_iocdata(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct copyresp *cp = (typeof(cp)) mp->b_rptr;
	mblk_t *dp;
	int err = 0;

	switch (mi_copy_state(q, mp, &dp)) {
	case MI_COPY_CASE(MI_COPY_IN, 1):
		err = nit_w_copyin(q, mp, xp, cp, dp);
		break;
	case MI_COPY_CASE(MI_COPY_OUT, 1):
		break;
	case -1:
		goto efault;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
      efault:
	return (0);
}

noinline fastcall __unlikely int
nit_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct iocblk *ioc = (typeof(ioc)) mp->b_rptr;
	int err = 0;

	switch (_IOC_TYPE(ioc->ioc_cmd)) {
	case _IOC_TYPE(NIOC):
		err = nit_ioctl(q, mp, xp, ioc);
		break;
	case _IOC_TYPE(SIOCGIFCONF):
		err = sio_ioctl(q, mp, xp, ioc);
		break;
	default:
		err = -EINVAL;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
	return (0);
}

#if 0
int
bind_it(struct xp *xp)
{
	struct cd *cd;
	struct sp *sp;
	struct ch *ch;
	psw_t flags = 0;
	int card, span, chan;

	switch (xp->monitor) {
	case XP_MONITOR_NONE:
		return (ENXIO);
	case XP_MONITOR_GLOB:
		read_lock_irqsave(&x400p_lock, flags);
		if ((xp->xray.next = x400p_xrays))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &x400p_xrays;
		x400p_xrays = xp;
		read_unlock_irqrestore(&x400p_lock, flags);
		for (card = 0; card < X400_CARDS; card++) {
			if (!(cd = x400p_cards[card]))
				continue;
			for (span = 0; span < cd->ports; span++) {
				if (!(sp = cd->spans[span]))
					continue;
				if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
					startup_span(sp);
				for (chan = 0; chan < 32; chan++) {
					if (!(ch = sp->chans[chan]))
						continue;
					spin_lock(&ch->lock);
					ch->xray.flags |= xp->xray.flags;
					if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
						sl_daedt_start(ch);
						sl_daedr_start(ch);
					}
					spin_unlock(&ch->lock);
				}
			}
		}
		break;

	case XP_MONITOR_CARD:
		spin_lock_irqsave(&cd->lock, flags);
		if ((xp->xray.next = cd->xray))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &cd->xray;
		cd->xray = xp;
		spin_unlock_irqrestore(&cd->lock, flags);
		for (span = 0; span < cd->ports; span++) {
			if ((sp = cd->spans[span])) {
				for (chan = 0; chan < 32; chan++) {
					if ((ch = sp->chans[chan])) {
						spin_lock_irqsave(&ch->lock, flags);
						ch->xray.flags |= xp->xray.flags;
						if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
							sl_daedt_start(ch);
							sl_daedr_start(ch);
						}
						spin_unlock_irqrestore(&ch->lock, flags);
					}
				}
				if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
					startup_span(sp);
			}
		}
		break;

	case XP_MONITOR_SPAN:
		xp->xray.flags = XP_XRAY_SDT_CHAN;
		spin_lock_irqsave(&sp->lock, flags);
		if ((xp->xray.next = sp->xray))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &sp->xray;
		sp->xray = xp;
		spin_unlock_irqrestore(&sp->lock, flags);
		for (chan = 0; chan < 32; chan++) {
			if ((ch = sp->chans[chan])) {
				spin_lock_irqsave(&ch->lock, flags);
				ch->xray.flags |= xp->xray.flags;
				if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
					sl_daedt_start(ch);
					sl_daedr_start(ch);
				}
				spin_unlock_irqrestore(&ch->lock, flags);
			}
		}
		if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
			startup_span(sp);
		break;

	case XP_MONITOR_CHAN:
		spin_lock_irqsave(&ch->lock, flags);
		if ((xp->xray.next = ch->xray.list))
			xp->xray.next->xray.prev = &xp->xray.next;
		xp->xray.prev = &ch->xray.list;
		ch->xray.list = xp;
		ch->xray.flags |= xp->xray.flags;
		if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7) {
			sl_daedt_start(ch);
			sl_daedr_start(ch);
		}
		spin_unlock_irqrestore(&ch->lock, flags);
		if (xp->level == XP_LEVEL_MON_SDT && sp->config.iftxlevel > 7)
			startup_span(sp);
		break;
	}
	return (0);
}
#endif

noinline fastcall __unlikely int
bpf_w_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_TYPE(cp->cp_cmd)) {
	case _IOC_TYPE(BIOCSBLEN):
		err = bpf_copyin(q, mp, xp, cp, dp);
		break;
	case _IOC_TYPE(SIOCGIFCONF):
		err = sio_copyin(q, mp, xp, cp, dp);
		break;
	default:
		err = -EOPNOTSUPP;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
bpf_w_iocdata(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct copyresp *cp = (typeof(cp)) mp->b_rptr;
	mblk_t *dp;
	int err = 0;

	switch (mi_copy_state(q, mp, &dp)) {
	case MI_COPY_CASE(MI_COPY_IN, 1):
		err = bpf_w_copyin(q, mp, xp, cp, dp);
		break;
	case MI_COPY_CASE(MI_COPY_OUT, 1):
		break;
	case MI_COPY_CASE(MI_COPY_OUT, 2):
		break;
	case -1:
		goto efault;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
      efault:
	return (0);
}

noinline fastcall __unlikely int
bpf_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct iocblk *ioc = (typeof(ioc)) mp->b_rptr;
	int err = 0;

	switch (_IOC_TYPE(ioc->ioc_cmd)) {
	case _IOC_TYPE(BIOCSBLEN):
		err = bpf_ioctl(q, mp, xp, ioc);
		break;
	case _IOC_TYPE(SIOCGIFCONF):
		err = sio_ioctl(q, mp, xp, ioc);
		break;
	default:
		err = -EINVAL;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
	return (0);
}

noinline fastcall __unlikely int
dl_w_copyin(queue_t *q, mblk_t *mp, struct xp *xp, struct copyresp *cp, mblk_t *dp)
{
	int err;

	switch (_IOC_TYPE(cp->cp_cmd)) {
	case _IOC_TYPE(DLIOC):
		err = dl_copyin(q, mp, xp, cp, dp);
		break;
	case _IOC_TYPE(NIOC):
		err = nit_copyin(q, mp, xp, cp, dp);
		break;
	default:
		err = -EINVAL;
		break;
	}
	return (err);
}

noinline fastcall __unlikely int
dl_w_iocdata(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct copyresp *cp = (typeof(cp)) mp->b_rptr;
	mblk_t *dp;
	int err = 0;

	switch (mi_copy_state(q, mp, &dp)) {
	case MI_COPY_CASE(MI_COPY_IN, 1):
		err = dl_w_copyin(q, mp, xp, cp, dp);
		break;
	case MI_COPY_CASE(MI_COPY_OUT, 1):
		break;
	case -1:
		goto efault;
	default:
		__swerr();
		err = -EPROTO;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
      efault:
	return (0);
}

noinline fastcall __unlikely int
dl_w_ioctl(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);
	struct iocblk *ioc = (typeof(ioc)) mp->b_rptr;
	int err;

	switch (_IOC_TYPE(ioc->ioc_cmd)) {
	case _IOC_TYPE(DLIOC):
		err = dl_ioctl(q, mp, xp, ioc);
		break;
	case _IOC_TYPE(NIOC):
		err = nit_ioctl(q, mp, xp, ioc);
		break;
	default:
		err = -EINVAL;
		break;
	}
	if (err > 0)
		mi_copyin(q, mp, NULL, err);
	else if (err < 0)
		mi_copy_done(q, mp, -err);
	else
		mi_copyout(q, mp);
	return (0);
}

/*
 *  M_PROTO, M_PCPROTO Handling
 *  -------------------------------------------------------------------------
 */
noinline fastcall __unlikely int
xp_w_proto_return(mblk_t *mp, int rtn)
{
	switch (-rtn) {
	case EBUSY:
	case EAGAIN:
	case ENOMEM:
	case ENOBUFS:
	case EDEADLK:
		return (rtn);
	default:
		freemsg(mp);
	case 0:
		return (0);
	}
}

noinline fastcall int
sl_w_proto_return(struct xp *xp, queue_t *q, mblk_t *mp, int rtn)
{
	int err = 0;

	switch (rtn) {
	case -EBUSY:
	case -EAGAIN:
	case -ENOMEM:
	case -ENOBUFS:
	case -EDEADLK:
		return (rtn);
	case -EFAULT:
	case -EPROTO:
	case -EMSGSIZE:
		err = -rtn;
		break;
	case 0:
		return (0);
	}
	if (err) {
		m_error(xp, q, mp, err);
		rtn = 0;
	}
	return (rtn);
}

noinline fastcall __unlikely int
lmi_w_proto_return(struct xp *xp, queue_t *q, mblk_t *mp, lmi_ulong prim, int rtn)
{
	if (unlikely(rtn < 0)) {
		lmi_ulong reason = 0;

		switch (rtn) {
		case -EINVAL:
			reason = LMI_BADPRIM;
			break;
		case -EOPNOTSUPP:
			reason = LMI_NOTSUPP;
			break;
		case -EMSGSIZE:
			reason = LMI_PROTOSHORT;
			break;
		case -EFAULT:
			reason = LMI_SYSERR;
			break;
		case -EPROTO:
			reason = LMI_OUTSTATE;
			break;
		case -EADDRNOTAVAIL:
			reason = LMI_BADPPA;
			break;
		}
		if (reason) {
			lmi_error_ack(xp, q, mp, xp_get_state(xp), prim, -rtn, reason);
			rtn = 0;
		}
	}
	return (rtn);
}

/** __sl_w_proto_slow: - process SLI primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SLI primitive
  * @prim: the SLI primitive type
  */
noinline fastcall int
__sl_w_proto_slow(struct xp *xp, queue_t *q, mblk_t *mp, sl_long prim)
{
	int rtn;

	if (likely(prim != SL_PDU_REQ)) {
		struct ch *ch;

		LOGRX(xp2sid(xp), "-> %s", sl_primname(prim));
		if (unlikely(xp_get_state(xp) != LMI_ENABLED))
			rtn = -EPROTO;
		else if (unlikely((ch = xp->ch) == NULL))
			rtn = -EFAULT;
		else {
			spin_lock_bh(&ch->lock);
			{
				switch (prim - SL_PROTO_BASE) {
				case (SL_EMERGENCY_REQ - SL_PROTO_BASE):
					rtn = sl_emergency_req(ch, q, mp);
					break;
				case (SL_EMERGENCY_CEASES_REQ - SL_PROTO_BASE):
					rtn = sl_emergency_ceases_req(ch, q, mp);
					break;
				case (SL_START_REQ - SL_PROTO_BASE):
					rtn = sl_start_req(ch, q, mp);
					break;
				case (SL_STOP_REQ - SL_PROTO_BASE):
					rtn = sl_stop_req(ch, q, mp);
					break;
				case (SL_RETRIEVE_BSNT_REQ - SL_PROTO_BASE):
					rtn = sl_retrieve_bsnt_req(ch, q, mp);
					break;
				case (SL_RETRIEVAL_REQUEST_AND_FSNC_REQ - SL_PROTO_BASE):
					rtn = sl_retrieval_request_and_fsnc_req(ch, q, mp);
					break;
				case (SL_CLEAR_BUFFERS_REQ - SL_PROTO_BASE):
					rtn = sl_clear_buffers_req(ch, q, mp);
					break;
				case (SL_CLEAR_RTB_REQ - SL_PROTO_BASE):
					rtn = sl_clear_rtb_req(ch, q, mp);
					break;
				case (SL_LOCAL_PROCESSOR_OUTAGE_REQ - SL_PROTO_BASE):
					rtn = sl_local_processor_outage_req(ch, q, mp);
					break;
				case (SL_CONTINUE_REQ - SL_PROTO_BASE):
					rtn = sl_continue_req(ch, q, mp);
					break;
				case (SL_RESUME_REQ - SL_PROTO_BASE):
					rtn = sl_resume_req(ch, q, mp);
					break;
				case (SL_CONGESTION_DISCARD_REQ - SL_PROTO_BASE):
					rtn = sl_congestion_discard_req(ch, q, mp);
					break;
				case (SL_CONGESTION_ACCEPT_REQ - SL_PROTO_BASE):
					rtn = sl_congestion_accept_req(ch, q, mp);
					break;
				case (SL_NO_CONGESTION_REQ - SL_PROTO_BASE):
					rtn = sl_no_congestion_req(ch, q, mp);
					break;
				case (SL_POWER_ON_REQ - SL_PROTO_BASE):
					rtn = sl_power_on_req(ch, q, mp);
					break;
				default:
					rtn = -EFAULT;
					break;
				}
			}
			spin_unlock_bh(&ch->lock);
		}
		rtn = sl_w_proto_return(xp, q, mp, rtn);
	} else {
		/* should have been processed in the fast path */
		LOGDA(xp2sid(xp), "-> SL_PDU_REQ");
		rtn = sl_pdu_req(xp, q, mp);
	}
	return (rtn);
}

/** __sdt_w_proto_slow: - process SDTI primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDTI primitive
  * @prim: the SDTI primitive type
  */
noinline fastcall int
__sdt_w_proto_slow(struct xp *xp, queue_t *q, mblk_t *mp, sdt_long prim)
{
	int rtn;

	if (likely(prim != SDT_DAEDT_TRANSMISSION_REQ)) {
		struct ch *ch;

		LOGRX(xp2sid(xp), "-> %s", sdt_primname(prim));
		if (unlikely(xp_get_state(xp) != LMI_ENABLED))
			rtn = -EPROTO;
		else if (unlikely((ch = xp->ch) == NULL))
			rtn = -EFAULT;
		else {
			spin_lock_bh(&ch->lock);
			{
				switch (prim - SDT_PROTO_BASE) {
				case (SDT_DAEDT_START_REQ - SDT_PROTO_BASE):
					rtn = sdt_daedt_start_req(ch, q, mp);
					break;
				case (SDT_DAEDR_START_REQ - SDT_PROTO_BASE):
					rtn = sdt_daedr_start_req(ch, q, mp);
					break;
				case (SDT_AERM_START_REQ - SDT_PROTO_BASE):
					rtn = sdt_aerm_start_req(ch, q, mp);
					break;
				case (SDT_AERM_STOP_REQ - SDT_PROTO_BASE):
					rtn = sdt_aerm_stop_req(ch, q, mp);
					break;
				case (SDT_AERM_SET_TI_TO_TIN_REQ - SDT_PROTO_BASE):
					rtn = sdt_aerm_set_ti_to_tin_req(ch, q, mp);
					break;
				case (SDT_AERM_SET_TI_TO_TIE_REQ - SDT_PROTO_BASE):
					rtn = sdt_aerm_set_ti_to_tie_req(ch, q, mp);
					break;
				case (SDT_SUERM_START_REQ - SDT_PROTO_BASE):
					rtn = sdt_suerm_start_req(ch, q, mp);
					break;
				case (SDT_SUERM_STOP_REQ - SDT_PROTO_BASE):
					rtn = sdt_suerm_stop_req(ch, q, mp);
					break;
				default:
					rtn = -EFAULT;
					break;
				}
			}
			spin_unlock_bh(&ch->lock);
		}
		rtn = sl_w_proto_return(xp, q, mp, rtn);
	} else {
		/* should have been processed in the fast path */
		LOGDA(xp2sid(xp), "-> SDT_DAEDT_TRANSMISSION_REQ");
		rtn = sdt_daedt_transmission_req(xp, q, mp);
	}
	return (rtn);
}

/** __sdl_w_proto_slow: - process SDLI primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the SDLI primitive
  * @prim: the SDLI primitive type
  */
noinline fastcall int
__sdl_w_proto_slow(struct xp *xp, queue_t *q, mblk_t *mp, sdl_long prim)
{
	int rtn;

	if (likely(prim != SDL_BITS_FOR_TRANSMISSION_REQ)) {
		struct ch *ch;

		LOGRX(xp2sid(xp), "-> %s", sdl_primname(prim));
		if (unlikely(xp_get_state(xp) != LMI_ENABLED))
			rtn = -EPROTO;
		else if (unlikely((ch = xp->ch) == NULL))
			rtn = -EFAULT;
		else {
			spin_lock_bh(&ch->lock);
			{
				switch (prim - SDL_PROTO_BASE) {
				case (SDL_CONNECT_REQ - SDL_PROTO_BASE):
					rtn = sdl_connect_req(ch, q, mp);
					break;
				case (SDL_DISCONNECT_REQ - SDL_PROTO_BASE):
					rtn = sdl_disconnect_req(ch, q, mp);
					break;
				default:
					rtn = -EFAULT;
					break;
				}
			}
			spin_unlock_bh(&ch->lock);
		}
		rtn = sl_w_proto_return(xp, q, mp, rtn);
	} else {
		/* should have been processed in the fast path */
		LOGDA(xp2sid(xp), "-> SDL_BITS_FOR_TRANSMISION_REQ");
		rtn = sdl_bits_for_transmission_req(xp, q, mp);
	}
	return (rtn);
}

/** __lmi_w_proto_slow: - process LMI primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the LMI primitive
  * @prim: the LMI primitive type
  */
noinline fastcall int
__lmi_w_proto_slow(struct xp *xp, queue_t *q, mblk_t *mp, lmi_long prim)
{
	int rtn;

	xp_save_state(xp);

	LOGRX(xp2sid(xp), "-> %s", lmi_primname(prim));
	switch (prim - LMI_PROTO_BASE) {
	case (LMI_INFO_REQ - LMI_PROTO_BASE):
		rtn = lmi_info_req(xp, q, mp);
		break;
	case (LMI_ATTACH_REQ - LMI_PROTO_BASE):
		rtn = lmi_attach_req(xp, q, mp);
		break;
	case (LMI_DETACH_REQ - LMI_PROTO_BASE):
		rtn = lmi_detach_req(xp, q, mp);
		break;
	case (LMI_ENABLE_REQ - LMI_PROTO_BASE):
		rtn = lmi_enable_req(xp, q, mp);
		break;
	case (LMI_DISABLE_REQ - LMI_PROTO_BASE):
		rtn = lmi_disable_req(xp, q, mp);
		break;
	case (LMI_OPTMGMT_REQ - LMI_PROTO_BASE):
		rtn = lmi_optmgmt_req(xp, q, mp);
		break;
	default:
		rtn = -EINVAL;
		break;
	}
	if (rtn < 0) {
		xp_restore_state(xp);
		/* The put and srv procedures do not recognize all errors.  Sometimes we return an error here 
		   just to restore the previous state. */
		return lmi_w_proto_return(xp, q, mp, prim, rtn);
	}
	return (rtn);
}

/** __xp_w_proto_slow: - process M_PROTO/M_PCPROTO message on write queue
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the M_(PC)PROTO message
  * @prim: the primitive type of the message
  */
noinline fastcall int
__xp_w_proto_slow(struct xp *xp, queue_t *q, mblk_t *mp, lmi_long prim)
{
	int rtn;

	if (SDL_DSTR_FIRST <= prim && prim <= SDL_DSTR_LAST) {
		rtn = __sdl_w_proto_slow(xp, q, mp, prim);
	} else if (SDT_DSTR_FIRST <= prim && prim <= SDT_DSTR_LAST) {
		rtn = __sdt_w_proto_slow(xp, q, mp, prim);
	} else if (SL_DSTR_FIRST <= prim && prim <= SL_DSTR_LAST) {
		rtn = __sl_w_proto_slow(xp, q, mp, prim);
	} else {
		rtn = __lmi_w_proto_slow(xp, q, mp, prim);
	}
	if (rtn < 0)
		return xp_w_proto_return(mp, rtn);
	return (rtn);
}

noinline fastcall int
xp_w_proto_slow(queue_t *q, mblk_t *mp, lmi_long prim)
{
	struct xp *xp = XP_PRIV(q);
	int err;

	if (likely((xp = (struct xp *) mi_acquire((caddr_t) xp, q)) != NULL)) {
		err = __xp_w_proto_slow(xp, q, mp, prim);
		mi_release((caddr_t) xp);
	} else
		err = -EDEADLK;
	return (err);
}

/** __xp_w_proto: - process M_PROTO or M_PCPROTO message locked
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the M_PROTO or M_PCPROTO message
  *
  * This locked version is for use by the service procedure that takes private structure locks once
  * for the entire service procedure run.  The fastpath for data should probably be ordered in the
  * reverse order (SDL priority before SDT or SL, because SDL has tighter latency constraints), but
  * it likely doesn't matter that much on a relatively fast machine.  The driver is more intended
  * for use as signalling links instead of as a raw channel driver.
  */
static inline fastcall __hot_write int
__xp_w_proto(struct xp *xp, queue_t *q, mblk_t *mp)
{
	if (likely(MBLKIN(mp, 0, sizeof(lmi_ulong)) != 0)) {
		lmi_long prim = *(lmi_long *) mp->b_rptr;
		int rtn;

		if (likely(prim == SL_PDU_REQ)) {
			if (likely((rtn = sl_pdu_req(xp, q, mp)) == 0))
				return (0);
		} else if (likely(prim == SDT_DAEDT_TRANSMISSION_REQ)) {
			if (likely((rtn = sdt_daedt_transmission_req(xp, q, mp)) == 0))
				return (0);
		} else if (likely(prim == SDL_BITS_FOR_TRANSMISSION_REQ)) {
			if (likely((rtn = sdl_bits_for_transmission_req(xp, q, mp)) == 0))
				return (0);
		} else
			return __xp_w_proto_slow(xp, q, mp, prim);
		return xp_w_proto_return(mp, rtn);
	}
	LOGRX(xp2sid(xp), "%s() replying with error %d", __FUNCTION__, -EPROTO);
	return m_error(xp, q, mp, -EPROTO);
}

/** xp_w_proto: - process M_PROTO or M_PCPROTO message
  * @q: active queue
  * @mp: the M_PROTO or M_PCPROTO message
  *
  * This lock version is for use by the put procedure which does not take locks.  For data messages
  * we simply return (-EAGAIN) for performance.
  */
static inline fastcall __hot_write int
xp_w_proto(queue_t *q, mblk_t *mp)
{
	if (likely(MBLKIN(mp, 0, sizeof(lmi_ulong)) != 0)) {
		lmi_long prim = *(lmi_long *) mp->b_rptr;

		if (likely(prim == SL_PDU_REQ)) {
		} else if (likely(prim == SDT_DAEDT_TRANSMISSION_REQ)) {
		} else if (likely(prim == SDL_BITS_FOR_TRANSMISSION_REQ)) {
		} else
			return xp_w_proto_slow(q, mp, prim);
	}
	return (-EAGAIN);
}

/** __nit_w_proto: - process M_PROTO message
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the M_PROTO message
  *
  * See nit_if(4).  nit accepts packets form the module above it in the stream and relays them to
  * the associated network interface for transmission.  Packets must be formatted with the
  * destination address in a leading M_PROTO(9) message block, followed by the packet itself,
  * complete with link-level header, in a sequence of M_DATA(9) message blocks.  The destination
  * address must be expressed as a struct sockaddr whose sa_family field is AF_UNSPEC and whose
  * sa_data field is a copy of the link-level header.  (See <sys/socket.h> for the definition of
  * this structure.)  If the packet does not conform to this format, an M_ERROR(9) message with
  * [EINVAL] will be sent upstream.
  */
static inline fastcall __hot_write int
__nit_w_proto(struct xp *xp, queue_t *q, mblk_t *mp)
{
	LOGRX(xp2sid(xp), "%s() M_PROTO/M_PCPROTO on write queue", __FUNCTION__);
	freemsg(mp);
	return (0);
}

static inline fastcall __hot_write int
nit_w_proto(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);

	return __nit_w_proto(xp, q, mp);
}

static inline fastcall __hot_write int
__bpf_w_proto(struct xp *xp, queue_t *q, mblk_t *mp)
{
	LOGRX(xp2sid(xp), "%s() M_PROTO/M_PCPROTO on write queue", __FUNCTION__);
	freemsg(mp);
	return (0);
}

static inline fastcall __hot_write int
bpf_w_proto(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);

	return __bpf_w_proto(xp, q, mp);
}

noinline fastcall int
__dl_w_proto_slow(struct xp *xp, queue_t *q, mblk_t *mp, dl_ulong prim)
{
	int rtn;

	xp_save_state(xp);

	switch (prim) {
	case DL_UNITDATA_REQ:
		LOGDA(xp2sid(xp), "-> %s", dlpi_primname(prim));
		break;
	default:
		LOGRX(xp2sid(xp), "-> %s", dlpi_primname(prim));
		break;
	}
	switch (prim) {
	case DL_INFO_REQ:
		rtn = dl_info_req(xp, q, mp);
		break;
	case DL_ATTACH_REQ:
		rtn = dl_attach_req(xp, q, mp);
		break;
	case DL_DETACH_REQ:
		rtn = dl_detach_req(xp, q, mp);
		break;
	case DL_BIND_REQ:
		rtn = dl_bind_req(xp, q, mp);
		break;
	case DL_UNBIND_REQ:
		rtn = dl_unbind_req(xp, q, mp);
		break;
	case DL_SUBS_BIND_REQ:
		rtn = dl_subs_bind_req(xp, q, mp);
		break;
	case DL_SUBS_UNBIND_REQ:
		rtn = dl_subs_unbind_req(xp, q, mp);
		break;
	case DL_UNITDATA_REQ:
		rtn = dl_unitdata_req(xp, q, mp);
		break;
	case DL_UDQOS_REQ:
		rtn = dl_udqos_req(xp, q, mp);
		break;
	case DL_CONNECT_REQ:
		rtn = dl_connect_req(xp, q, mp);
		break;
	case DL_CONNECT_RES:
		rtn = dl_connect_res(xp, q, mp);
		break;
	case DL_TOKEN_REQ:
		rtn = dl_token_req(xp, q, mp);
		break;
	case DL_DISCONNECT_REQ:
		rtn = dl_disconnect_req(xp, q, mp);
		break;
	case DL_RESET_REQ:
		rtn = dl_reset_req(xp, q, mp);
		break;
	case DL_RESET_RES:
		rtn = dl_reset_res(xp, q, mp);
		break;
	case DL_ENABMULTI_REQ:
		rtn = dl_enabmulti_req(xp, q, mp);
		break;
	case DL_DISABMULTI_REQ:
		rtn = dl_disabmulti_req(xp, q, mp);
		break;
	case DL_PROMISCON_REQ:
		rtn = dl_promiscon_req(xp, q, mp);
		break;
	case DL_PROMISCOFF_REQ:
		rtn = dl_promiscoff_req(xp, q, mp);
		break;
	case DL_XID_REQ:
		rtn = dl_xid_req(xp, q, mp);
		break;
	case DL_XID_RES:
		rtn = dl_xid_res(xp, q, mp);
		break;
	case DL_TEST_REQ:
		rtn = dl_test_req(xp, q, mp);
		break;
	case DL_TEST_RES:
		rtn = dl_test_res(xp, q, mp);
		break;
	case DL_PHYS_ADDR_REQ:
		rtn = dl_phys_addr_req(xp, q, mp);
		break;
	case DL_SET_PHYS_ADDR_REQ:
		rtn = dl_set_phys_addr_req(xp, q, mp);
		break;
	case DL_GET_STATISTICS_REQ:
		rtn = dl_get_statistics_req(xp, q, mp);
		break;
	case DL_MONITOR_LINK_LAYER:
		rtn = dl_monitor_link_layer(xp, q, mp);
		break;
#ifdef _SUN_SOURCE
#ifdef DL_NOTIFY_REQ
	case DL_NOTIFY_REQ:
		rtn = dl_notify_req(xp, q, mp);
		break;
#endif				/* DL_NOTIFY_REQ */
#ifdef DL_AGGR_REQ
	case DL_AGGR_REQ:
		rtn = dl_aggr_req(xp, q, mp);
		break;
#endif				/* DL_AGGR_REQ */
#ifdef DL_UNAGGR_REQ
	case DL_UNAGGR_REQ:
		rtn = dl_unaggr_req(xp, q, mp);
		break;
#endif				/* DL_UNAGGR_REQ */
#ifdef DL_CAPABILITY_REQ
	case DL_CAPABILITY_REQ:
		rtn = dl_capability_req(xp, q, mp);
		break;
#endif				/* DL_CAPABILITY_REQ */
#ifdef DL_CONTROL_REQ
	case DL_CONTROL_REQ:
		rtn = dl_control_req(xp, q, mp);
		break;
#endif				/* DL_CONTROL_REQ */
#ifdef DL_PASSIVE_REQ
	case DL_PASSIVE_REQ:
		rtn = dl_passive_req(xp, q, mp);
		break;
#endif				/* DL_PASSIVE_REQ */
#ifdef DL_INTR_MODE_REQ
	case DL_INTR_MODE_REQ:
		rtn = dl_intr_mode_req(xp, q, mp);
		break;
#endif				/* DL_INTR_MODE_REQ */
#endif				/* SUN_SOURCE */
#ifdef _HPUX_SOURCE
#ifdef DL_HP_PPA_REQ
	case DL_HP_PPA_REQ:
		rtn = dl_hp_ppa_req(xp, q, mp);
		break;
#endif				/* DL_HP_PPA_REQ */
#ifdef DL_HP_MULTICAST_LIST_REQ
	case DL_HP_MULTICAST_LIST_REQ:
		rtn = dl_hp_multicast_list_req(xp, q, mp);
		break;
#endif				/* DL_HP_MULTICAST_LIST_REQ */
#ifdef DL_HP_RAWDATA_REQ
	case DL_HP_RAWDATA_REQ:
		rtn = dl_hp_rawdata_req(xp, q, mp);
		break;
#endif				/* DL_HP_RAWDATA_REQ */
#ifdef DL_HP_HW_RESET_REQ
	case DL_HP_HW_RESET_REQ:
		rtn = dl_hp_hw_reset_req(xp, q, mp);
		break;
#endif				/* DL_HP_HW_RESET_REQ */
#endif				/* _HPUX_SOURCE */
	default:
		rtn = -EBADMSG;
		break;
	}
	if (rtn < 0) {
		ulong reason = 0;

		xp_restore_state(xp);
		switch (rtn) {
		case -EAFNOSUPPORT:
			reason = DL_BADSAP;
			break;
		case -EPROTONOSUPPORT:
			reason = DL_UNSUPPORTED;
			break;
		case -EOPNOTSUPP:
			reason = DL_NOTSUPPORTED;
			break;
		case -EBADMSG:
		case -EMSGSIZE:
			reason = DL_BADPRIM;
			break;
		case -EFAULT:
			reason = DL_SYSERR;
			break;
		case -EPROTO:
			reason = DL_OUTSTATE;
			break;
		case -EADDRNOTAVAIL:
			reason = DL_BADPPA;
			break;
		case -ENOENT:
			reason = DL_NOTENAB;
			break;
		case -EPERM:
			reason = DL_ACCESS;
			break;
		}
		if (reason) {
			dl_error_ack(xp, q, mp, prim, -rtn, reason);
			rtn = 0;
		}
	}
	/* The put and srv procedures do not recognize all errors.  Sometimes we return an error here just to 
	   restore the previous state. */
	return xp_w_proto_return(mp, rtn);
}

noinline fastcall int
dl_w_proto_slow(queue_t *q, mblk_t *mp, dl_ulong prim)
{
	struct xp *xp = XP_PRIV(q);
	int err;

	if (likely((xp = (struct xp *) mi_acquire((caddr_t) xp, q)) != NULL)) {
		err = __dl_w_proto_slow(xp, q, mp, prim);
		mi_release((caddr_t) xp);
	} else
		err = -EDEADLK;
	return (err);
}

/** __dl_w_proto: - process M_PROTO or M_PCPROTO message locked
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the M_PROTO or M_PCPROTO message
  *
  * This locked version is for use by the service procedure that takes private structure locks once
  * for the entire service procedure run.  Keep this out of the fast path because DLPI uses M_DATA
  * for DL_CODLS data transfer.
  */
noinline fastcall int
__dl_w_proto(struct xp *xp, queue_t *q, mblk_t *mp)
{
	if (likely(mp->b_wptr >= mp->b_rptr + sizeof(dl_ulong))) {
		dl_ulong prim = *(dl_ulong *) mp->b_rptr;

		return __dl_w_proto_slow(xp, q, mp, prim);
	}
	LOGRX(xp2sid(xp), "%s() replying with error %d", __FUNCTION__, -EPROTO);
	return m_error(xp, q, mp, -EPROTO);
}

/** dl_w_proto: - process M_PROTO or M_PCPROTO message
  * @q: active queue (write queue)
  * @mp: the M_PROTO or M_PCPROTO message
  *
  * This lock version is for use by the put procedure which does not take locks.  For data messages
  * we simply return (-EAGAIN) for performance.  Keep this out of the fast path because DLPI uses
  * M_DATA for DL_CODLS data transfer.
  */
noinline fastcall int
dl_w_proto(queue_t *q, mblk_t *mp)
{
	if (likely(mp->b_wptr >= mp->b_rptr + sizeof(dl_ulong))) {
		dl_ulong prim = *(dl_ulong *) mp->b_rptr;

		return dl_w_proto_slow(q, mp, prim);
	}
	return (-EAGAIN);
}

#if 0
/*
 *  M_SIG/M_PCSIG Handling
 *  -------------------------------------------------------------------------
 */

/** __xp_r_sig: - process M_(PC)SIG message
  * @xp: private structure (locked)
  * @q: active queue (read queue)
  * @mp: the M_(PC)SIG message
  *
  * This really needs to be worked back to the point where the timeout invocations of the state
  * machine are independent of any Streams queue being present.  This is because we would like the
  * signalling link state machine to be able to run without an attached Stream.  This would permit
  * placing a signalling link into a local processor outage state when a Stream closes unexpectedly.
  */
static noinline fastcall __unlikely int
__xp_r_sig(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;

	if (likely(mi_timer_valid(mp))) {
		if (likely((ch = xp->ch) != NULL)) {
			const int timer = *(int *) mp->b_rptr;

			spin_lock_bh(&ch->lock);
			{
				switch (timer) {
				case t1:
					LOGTO(ch2sid(ch), "-> T1 TIMEOUT <-");
					xp_t1_timeout(ch);
					break;
				case t2:
					LOGTO(ch2sid(ch), "-> T2 TIMEOUT <-");
					xp_t2_timeout(ch);
					break;
				case t3:
					LOGTO(ch2sid(ch), "-> T3 TIMEOUT <-");
					xp_t3_timeout(ch);
					break;
				case t4:
					LOGTO(ch2sid(ch), "-> T4 TIMEOUT <-");
					xp_t4_timeout(ch);
					break;
				case t5:
					LOGTO(ch2sid(ch), "-> T5 TIMEOUT <-");
					xp_t5_timeout(ch);
					break;
				case t6:
					LOGTO(ch2sid(ch), "-> T6 TIMEOUT <-");
					xp_t6_timeout(ch);
					break;
				case t7:
					LOGTO(ch2sid(ch), "-> T7 TIMEOUT <-");
					xp_t7_timeout(ch);
					break;
				case t8:
					LOGTO(ch2sid(ch), "-> T8 TIMEOUT <-");
					xp_t8_timeout(ch);
					break;
#if 0
				case t9:
					LOGTO(ch2sid(ch), "-> T9 TIMEOUT <-");
					xp_t9_timeout(xp);
					break;
#endif
				default:
					LOGERR(xp2sid(xp), "SWERR: invalid M_SIG/M_PCSIG on read queue");
					break;
				}
			}
			spin_unlock_bh(&ch->lock);
		} else
			LOGERR(xp2sid(xp), "SWERR: timeout with not attached channel");
	}
	return (0);
}
#endif

/*
 *  M_DATA Handling
 *  -------------------------------------------------------------------------
 */
/** __xp_w_data: - process M_DATA message
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the M_DATA message
  *
  * This function either returns zero (0) when the message is consumed, or a negative error number
  * when the message is to be (re)queued.  This non-locking version is used by the service
  * procedure.
  */
static inline fastcall __hot_write int
__xp_w_data(struct xp *xp, queue_t *q, mblk_t *mp)
{
	return xp_send_data(xp, q, mp);
}

/** xp_w_data: - process M_DATA message (put procedure)
  * @q: active queue (write queue)
  * @mp: the M_DATA message
  *
  * This function always returns -%EAGAIN for performance.  This version is used by the put
  * procedure.  Note that we do not actually call this function: we use a fastpath check for M_DATA
  * message types.  See xp_wput().
  */
static inline fastcall __hot_write int
xp_w_data(queue_t *q, mblk_t *mp)
{
	/* Always queue from put procedure for performance. */
	return (-EAGAIN);
}

/** __xp_r_data: - process M_DATA message (service procedure)
  * @xp: private structure (locked)
  * @q: active queue (read queue)
  * @mp: the M_DATA message
  *
  * Note that SL-level packets are always sent upstream as M_DATA messages.  SDT-level packets are
  * always sent as M_DATA when they do not have a repetition number, M_PROTO otherwise.  SDL-level
  * packets are always sent as M_DATA.
  */
noinline fastcall __hot_read int
__xp_r_data(struct xp *xp, queue_t *q, mblk_t *mp)
{
	struct ch *ch;

	if (likely((ch = xp->ch) != NULL)) {
		if (likely(canputnext(q))) {
			if (likely(xp->monitor == 0)) {
				/* we should likely make this an atomic integer */
				spin_lock_bh(&ch->lock);
				ch->sl.statem.Cr--;
				spin_unlock_bh(&ch->lock);
			}
			putnext(q, mp);
			return (0);
		}
		return (-EBUSY);
	}
	/* discard extra message */
	freemsg(mp);
	return (0);
}

/*
 *  M_FLUSH Handling
 *  -------------------------------------------------------------------------
 */

/** xp_w_flush: - process M_FLUSH
  * @q: active queue (write queue)
  * @mp: the M_FLUSH message
  *
  * This canonical driver write flush procedure flushes the write side queue if requested and
  * cancels write flush.  If read side flush is also requeested, the read side is flushed and the
  * message is passed upward.  If read side flush is not requested, the message is freeed.  There is
  * the added wrinkle that FISU/LSSU compression buffers are cleared on the transmit and the receive
  * when the corresponding queue is flushed.
  *
  * There is a little bit of a problem here: the dereference to acquire the xp pointer is ok because
  * the private structure cannot disappear while the flush is being processed, however, the xp->ch
  * dereference is problematic: the pointer might exist at one point and not at another because the
  * xp structure is not locked (has not been acquired).  If we are going to support ch structures
  * going away while streams are open, this could cause crashes.
  */
noinline fastcall __unlikely int
xp_w_flush(queue_t *q, mblk_t *mp)
{
	struct xp *xp = XP_PRIV(q);

	if (mp->b_rptr[0] & FLUSHW) {
		struct ch *ch;

		if ((ch = xp->ch)) {
			spin_lock_bh(&ch->lock);
			if (ch->tx.cmp) {
				ch->tx.cmp = NULL;
				ch->tx.repeat = 0;
			}
			spin_unlock_bh(&ch->lock);
		}
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(q, mp->b_rptr[1], FLUSHDATA);
		else
			flushq(q, FLUSHDATA);
		mp->b_rptr[0] &= ~FLUSHW;
	}
	if (mp->b_rptr[0] & FLUSHR) {
		struct ch *ch;

		if ((ch = xp->ch)) {
			spin_lock_bh(&ch->lock);
			if (ch->rx.cmp) {
				freeb(ch->rx.cmp);
				ch->rx.cmp = NULL;
				ch->rx.repeat = 0;
			}
			spin_unlock_bh(&ch->lock);
		}
		if (mp->b_rptr[0] & FLUSHBAND)
			flushband(OTHERQ(q), mp->b_rptr[1], FLUSHDATA);
		else
			flushq(OTHERQ(q), FLUSHDATA);
		qreply(q, mp);
		return (0);
	}
	freemsg(mp);
	return (0);
}

/*
 *  =========================================================================
 *
 *  WAKEUPS
 *
 *  =========================================================================
 */
noinline fastcall void
__xp_r_wakeup(struct xp *xp, queue_t *q)
{
	if (unlikely(xp->i_flags == 0))
		return;
	/* FIXME: when a flag is set, attempt to perform the action */
}

/*
 *  =========================================================================
 *
 *  PUT and SRV
 *
 *  =========================================================================
 */
STATIC streamscall __hot_in int
xp_r_prim_srv(struct xp *xp, queue_t *q, mblk_t *mp)
{
	switch (__builtin_expect(DB_TYPE(mp), M_DATA)) {
	case M_DATA:
		return __xp_r_data(xp, q, mp);
#if 0
	case M_SIG:
	case M_PCSIG:
		return __xp_r_sig(xp, q, mp);
#endif
	default:
		if (DB_TYPE(mp) >= QPCTL || bcanputnext(q, mp->b_band)) {
			putnext(q, mp);
			return (0);
		}
		return (-EBUSY);
	}
}

#ifndef DL_NODE
#define DL_NODE 5
#endif

/** xp_w_other: - put or service a primitive
  * @q: active queue (write queue)
  * @mp: the primitive
  *
  * This is the slow path for the write put procedure.  These should happen rarely.  We keep this
  * out of line and in the unlikely text block.
  */
noinline fastcall __unlikely int
xp_w_other(queue_t *q, mblk_t *mp)
{
	switch (__builtin_expect(DB_TYPE(mp), M_FLUSH)) {
	case M_FLUSH:
		return xp_w_flush(q, mp);
	case M_IOCTL:
		switch (XP_PRIV(q)->bminor) {
		case 0:
		default:
			return xp_w_ioctl(q, mp);
		case DL_NODE:
			return dl_w_ioctl(q, mp);
		case BPF_NODE:
			return bpf_w_ioctl(q, mp);
		case NIT_NODE:
			return nit_w_ioctl(q, mp);
		}
	case M_IOCDATA:
		switch (XP_PRIV(q)->bminor) {
		case 0:
		default:
			return xp_w_iocdata(q, mp);
		case DL_NODE:
			return dl_w_iocdata(q, mp);
		case BPF_NODE:
			return bpf_w_iocdata(q, mp);
		case NIT_NODE:
			return nit_w_iocdata(q, mp);
		}
	}
	LOGERR(XP_PRIV(q)->cminor, "SWERR: %s %s:%d", __FUNCTION__, __FILE__, __LINE__);
	freemsg(mp);
	return (0);
}

/** xp_w_prim_put: - put a primitive
  * @q: active queue (write queue)
  * @mp: the primitive
  *
  * This is the fast path for the write put procedure.  Data is simply queue by returning -%EAGAIN.
  * This provides better flow control and scheduling for maximum throughput.
  */
static inline streamscall __hot_put int
xp_w_prim_put(queue_t *q, mblk_t *mp)
{
	if (likely(DB_TYPE(mp) == M_DATA))
		return xp_w_data(q, mp);

	switch (__builtin_expect(DB_TYPE(mp), M_PROTO)) {
	case M_PROTO:
	case M_PCPROTO:
		switch (__builtin_expect(XP_PRIV(q)->bminor, 0)) {
		case 0:
		default:
			return xp_w_proto(q, mp);
		case DL_NODE:
			return dl_w_proto(q, mp);
		case BPF_NODE:
			return bpf_w_proto(q, mp);
		case NIT_NODE:
			return nit_w_proto(q, mp);
		}
	default:
		return xp_w_other(q, mp);
	}
}

/** xp_w_prim_srv: - service a primitive
  * @xp: private structure (locked)
  * @q: active queue (write queue)
  * @mp: the primitive
  *
  * This is the fast path for the write service procedure.  Data is processed.  Processing data form
  * the write service procedure provides better flow control and scheduling for maximum throughput
  * and minimum latency.
  */
static inline streamscall __hot_put int
xp_w_prim_srv(struct xp *xp, queue_t *q, mblk_t *mp)
{
	if (likely(DB_TYPE(mp) == M_DATA))
		return __xp_w_data(xp, q, mp);

	switch (__builtin_expect(DB_TYPE(mp), M_PROTO)) {
	case M_PROTO:
	case M_PCPROTO:
		switch (__builtin_expect(xp->bminor, 0)) {
		case 0:
		default:
			return __xp_w_proto(xp, q, mp);
		case DL_NODE:
			return __dl_w_proto(xp, q, mp);
		case BPF_NODE:
			return __bpf_w_proto(xp, q, mp);
		case NIT_NODE:
			return __nit_w_proto(xp, q, mp);
		}
	default:
		return xp_w_other(q, mp);
	}
}

/*
 *  =========================================================================
 *
 *  QUEUE PUT and SERVICE routines
 *
 *  =========================================================================
 */
/** xp_rput: - read put procedure
  * @q: active queue (read queue)
  * @mp: message to put
  *
  * There are several reasons for using a read put and service procedure in the driver.  One is that
  * we want to monitor upstream flow control by the depth of the queue instead of just with
  * canputnext.  However, we do not do any checking but merely put the message on the queue.  Note
  * that timers messages are inserted into the queue instead of calling put(9) so we won't see them
  * here.
  */
STATIC streamscall __hot_out int
xp_rput(queue_t *q, mblk_t *mp)
{
	xp_rstat.ms_acnt++;
	if (unlikely(!putq(q, mp))) {
		mp->b_band = 0;
		putq(q, mp);	/* must succeed */
	}
	return (0);
}

/** xp_rsrv: - read service procedure
  * @q: active queue (read queue)
  *
  * This is a canonical service procedure for read.  Locking is performed outside the loop so that
  * locks do not need to be released and acquired with each loop.  Note that the wakeup function
  * must also be exectued with the private structure locked.
  *
  * There are several reasons for using a read service procedure in the driver.  One is that we want
  * timer messages to be placed on the queue and the service procedure is the only way of processing
  * them.
  */
STATIC streamscall __hot_in int
xp_rsrv(queue_t *q)
{
	struct xp *xp = XP_PRIV(q);

	if (likely((xp = (struct xp *) mi_acquire((caddr_t) xp, q)) != NULL)) {
		mblk_t *mp;

		while (likely((mp = getq(q)) != NULL)) {
			if (unlikely(xp_r_prim_srv(xp, q, mp))) {
				putbq(q, mp);
				break;
			}
		}
		if (unlikely(xp->i_flags != 0))
			__xp_r_wakeup(xp, q);
		mi_release((caddr_t) xp);
	}
	return (0);
}

/** xp_wput: - write put procedure
  * @q: active queue (write queue)
  * @mp: message to put
  *
  * This is a canonical put procedure for write.  Locking is performed by the individual message
  * handling procedures.  Note that M_DATA messages are always queued for performance.  Therefore,
  * everywhere we call put(9) to place an M_DATA message on the queue, this function queues the
  * M_DATA and does not process it immediately.
  */
STATIC streamscall __hot_in int
xp_wput(queue_t *q, mblk_t *mp)
{
	if (likely(DB_TYPE(mp) == M_DATA)
	    || unlikely(DB_TYPE(mp) < QPCTL && (q->q_first || (q->q_flag & QSVCBUSY)))
	    || unlikely(xp_w_prim_put(q, mp))) {
		xp_wstat.ms_acnt++;
		if (unlikely(!putq(q, mp))) {
			mp->b_band = 0;
			putq(q, mp);	/* must succeed */
		}
	}
	return (0);
}

/** xp_wsrv: - write service procedure
  * @q: active queue (write queue)
  *
  * This is a canonical service procedure for write.  Locking is performed outside the loop so that
  * locks do not need to be released and reacquired with each loop.  Note that the wakeup function
  * must also be executed with the private structure locked.
  */
STATIC streamscall __hot_in int
xp_wsrv(queue_t *q)
{
	struct xp *xp = XP_PRIV(q);

	if (likely((xp = (struct xp *) mi_acquire((caddr_t) xp, q)) != NULL)) {
		mblk_t *mp;

		while (likely((mp = getq(q)) != NULL)) {
			if (unlikely(xp_w_prim_srv(xp, q, mp))) {
				putbq(q, mp);
				break;
			}
		}
		mi_release((caddr_t) xp);
	}
	return (0);
}

/*
 *  =========================================================================
 *
 *  OPEN and CLOSE
 *
 *  =========================================================================
 *  Open is called on the first open of a character special device stream
 *  head; close is called on the last close of the same device.
 */

#define FIRST_CMINOR	 0
#define LAST_CMINOR	10
#define FREE_CMINOR	11

STATIC kmem_cachep_t xp_priv_cachep;

static caddr_t xp_opens = NULL;
STATIC major_t xp_majors[CMAJORS] = { CMAJOR_0, };

/** xp_qopen: - each open of a stream
  * @q: queue pair
  * @devp: device pointer
  * @flag: open flags
  * @sflag: STREAMS flag
  * @crp: credentials pointer
  */
STATIC streamscall int
xp_qopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
	minor_t bminor = getminor(*devp);
	psw_t flags;
	int err;

	if (q->q_ptr)
		return (0);	/* already open */
	if (sflag != CLONEOPEN)
		return (ENXIO);
	if (bminor < FIRST_CMINOR || bminor > LAST_CMINOR)
		return (ENXIO);
	*devp = makedevice(getmajor(*devp), FREE_CMINOR);
	if (!mi_set_sth_lowat(q, 0))
		return (ENOBUFS);
	if (!mi_set_sth_hiwat(q, SHEADHIWAT >> 1))
		return (ENOBUFS);
	write_lock_irqsave(&xp_list_lock, flags);
	err = mi_open_comm_cache(&xp_opens, xp_priv_cachep, q, devp, flag, sflag, crp);
	write_unlock_irqrestore(&xp_list_lock, flags);
	if (err)
		return (err);
	{
		struct xp *xp = XP_PRIV(q);

		/* fill out xp structure and perform other allocations */
		bzero(xp, sizeof(*xp));
		xp->rq = RD(q);
		xp->wq = WR(q);
		xp->cmajor = getmajor(*devp);
		xp->cminor = getminor(*devp);
		xp->bminor = bminor;
		switch (bminor) {
		default:
		case 0:
			xp->i_version = 1;
			xp->i_style = LMI_STYLE2;
			xp->i_state = LMI_UNATTACHED;
			break;
		case BPF_NODE:
			xp->i_version = 1;
			xp->i_style = LMI_STYLE2;
			xp->i_state = 0;
			break;
		case NIT_NODE:
			xp->i_version = 1;
			xp->i_style = LMI_STYLE2;
			xp->i_state = 0;
			break;
		case DL_NODE:
			xp->i_version = DL_CURRENT_VERSION;
			xp->i_style = DL_STYLE2;
			xp->i_state = DL_UNATTACHED;
			break;
		}
	}
	qprocson(q);
	return (0);
}

/** xp_qclose: - last close of a stream
  * @q: queue pair
  * @flag: open flags
  * @crp: credentials
  */
STATIC streamscall __unlikely int
xp_qclose(queue_t *q, int flag, cred_t *crp)
{
	struct xp *xp = XP_PRIV(q);
	caddr_t ptr = (caddr_t) xp;
	struct ch *ch;
	psw_t flags;
	int err;

	qprocsoff(q);
	write_lock_irqsave(&xp_list_lock, flags);
	mi_acquire_sleep_nosignal(ptr, &ptr, &xp_list_lock, &flags);
	if ((ch = xp->ch)) {
		xp->ch = NULL;
		spin_lock(&ch->lock);
		{
			if (ch->xp == xp) {
				ch->xp = NULL;
				ch->sdl.config.ifflags &= ~SDL_IF_UP;
				/* FIXME FIXME FIXME: more to do here: worry about the state of the state
				   machine, perform local processor outage if necessary, etc. */
			}
			ch_put(ch);
		}
		spin_unlock(&ch->lock);
	}
	mi_release(ptr);
	err = mi_close_comm_cache(&xp_opens, xp_priv_cachep, q);
	write_unlock_irqrestore(&xp_list_lock, flags);
	return (err);
}

/*
 *  ==========================================================================
 *
 *  Private and Card structure allocation and deallocation
 *
 *  ==========================================================================
 */

STATIC kmem_cachep_t xp_priv_cachep = NULL;
STATIC kmem_cachep_t xp_sync_cachep = NULL;
STATIC kmem_cachep_t xp_chan_cachep = NULL;
STATIC kmem_cachep_t xp_span_cachep = NULL;
STATIC kmem_cachep_t xp_card_cachep = NULL;
STATIC kmem_cachep_t xp_xbuf_cachep = NULL;

/*
 *  Cache allocation
 *  -------------------------------------------------------------------------
 */
noinline int
xp_term_caches(void)
{
	int err = 0;

	if (xp_xbuf_cachep) {
#ifdef HAVE_KTYPE_KMEM_CACHE_T_P
		if (kmem_cache_destroy(xp_xbuf_cachep)) {
			cmn_err(CE_WARN, "%s: did not destroy xp_xbuf_cachep", __FUNCTION__);
			err = -EBUSY;
		} else
			printd(("%s: shrunk xp_xbuf_cache to zero\n", DRV_NAME));
#else
		kmem_cache_destroy(xp_xbuf_cachep);
#endif
	}
	if (xp_sync_cachep) {
#ifdef HAVE_KTYPE_KMEM_CACHE_T_P
		if (kmem_cache_destroy(xp_sync_cachep)) {
			cmn_err(CE_WARN, "%s: did not destroy xp_sync_cachep", __FUNCTION__);
			err = -EBUSY;
		} else
			printd(("%s: shrunk xp_sync_cache to zero\n", DRV_NAME));
#else
		kmem_cache_destroy(xp_sync_cachep);
#endif
	}
	if (xp_card_cachep) {
#ifdef HAVE_KTYPE_KMEM_CACHE_T_P
		if (kmem_cache_destroy(xp_card_cachep)) {
			cmn_err(CE_WARN, "%s: did not destroy xp_card_cachep", __FUNCTION__);
			err = -EBUSY;
		} else
			printd(("%s: shrunk xp_card_cache to zero\n", DRV_NAME));
#else
		kmem_cache_destroy(xp_card_cachep);
#endif
	}
	if (xp_span_cachep) {
#ifdef HAVE_KTYPE_KMEM_CACHE_T_P
		if (kmem_cache_destroy(xp_span_cachep)) {
			cmn_err(CE_WARN, "%s: did not destroy xp_span_cachep", __FUNCTION__);
			err = -EBUSY;
		} else
			printd(("%s: shrunk xp_span_cache to zero\n", DRV_NAME));
#else
		kmem_cache_destroy(xp_span_cachep);
#endif
	}
	if (xp_chan_cachep) {
#ifdef HAVE_KTYPE_KMEM_CACHE_T_P
		if (kmem_cache_destroy(xp_chan_cachep)) {
			cmn_err(CE_WARN, "%s: did not destroy xp_chan_cachep", __FUNCTION__);
			err = -EBUSY;
		} else
			printd(("%s: shrunk xp_slot_cache to zero\n", DRV_NAME));
#else
		kmem_cache_destroy(xp_chan_cachep);
#endif
	}
	if (xp_priv_cachep) {
#ifdef HAVE_KTYPE_KMEM_CACHE_T_P
		if (kmem_cache_destroy(xp_priv_cachep)) {
			cmn_err(CE_WARN, "%s: did not destroy xp_priv_cachep", __FUNCTION__);
			err = -EBUSY;
		} else
			printd(("%s: shrunk xp_priv_cache to zero\n", DRV_NAME));
#else
		kmem_cache_destroy(xp_priv_cachep);
#endif
	}
	return (err);
}

noinline __devinit int
xp_init_caches(void)
{
	if (!xp_priv_cachep
	    && !(xp_priv_cachep =
		 kmem_create_cache("xp_priv_cachep", mi_open_size(sizeof(struct xp)), 0, SLAB_HWCACHE_ALIGN, NULL,
				   NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate xp_priv_cachep", __FUNCTION__);
		goto error;
	} else
		printd(("%s: initialized device private structure cache\n", DRV_NAME));
	if (!xp_chan_cachep
	    && !(xp_chan_cachep =
		 kmem_create_cache("xp_chan_cachep", sizeof(struct ch), 0, SLAB_HWCACHE_ALIGN, NULL,
				   NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate xp_chan_cachep", __FUNCTION__);
		goto error;
	} else
		printd(("%s: initialized chan private structure cache\n", DRV_NAME));
	if (!xp_span_cachep
	    && !(xp_span_cachep =
		 kmem_create_cache("xp_span_cachep", sizeof(struct sp), 0, SLAB_HWCACHE_ALIGN, NULL,
				   NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate xp_span_cachep", __FUNCTION__);
		goto error;
	} else
		printd(("%s: initialized span private structure cache\n", DRV_NAME));
	if (!xp_card_cachep
	    && !(xp_card_cachep =
		 kmem_create_cache("xp_card_cachep", sizeof(struct cd), 0, SLAB_HWCACHE_ALIGN, NULL,
				   NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate xp_card_cachep", __FUNCTION__);
		goto error;
	} else
		printd(("%s: initialized card private structure cache\n", DRV_NAME));
	if (!xp_sync_cachep
	    && !(xp_sync_cachep =
		 kmem_create_cache("xp_sync_cachep", sizeof(struct sg), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate xp_sync_cachep", __FUNCTION__);
		goto error;
	} else
		printd(("%s: initialized sync private structure cache\n", DRV_NAME));
	if (!xp_xbuf_cachep
	    && !(xp_xbuf_cachep =
		 kmem_create_cache("xp_xbuf_cachep", X400P_EBUFNO * 1024, 0, SLAB_HWCACHE_ALIGN,
				   NULL, NULL))) {
		cmn_err(CE_PANIC, "%s: Cannot allocate xp_xbuf_cachep", __FUNCTION__);
		goto error;
	} else
		printd(("%s: initialized card read/write buffer cache\n", DRV_NAME));
	return (0);
      error:
	return (-EFAULT);
}

/*
 *  Channel allocation and deallocation
 *  -------------------------------------------------------------------------
 */
noinline __unlikely void
xp_init_ch(struct sp *sp, struct ch *ch, uint8_t chan)
{
	ch->ppa = ((chan & 0xff) << 0) | ((sp->span & 0x0f) << 8) | ((sp->cd->card & 0x0f) << 12);
	ch->chan = chan;
	/* fill out channel structure */
	printd(("%s: linked channel private structure\n", DRV_NAME));
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		if (chan != 0) {
			ch->option = lmi_default_e1_chan;
			ch->sdl.config = sdl_default_e1_chan;
			ch->sdt.config = sdt_default_e1_chan;
			ch->sl.config = sl_default_e1_chan;
		} else {
			ch->option = lmi_default_e1_span;
			ch->sdl.config = sdl_default_e1_span;
			ch->sdt.config = sdt_default_e1_span;
			ch->sl.config = sl_default_e1_span;
		}
		break;
	case SDL_GTYPE_T1:
		if (chan != 0) {
			ch->option = lmi_default_t1_chan;
			ch->sdl.config = sdl_default_t1_chan;
			ch->sdt.config = sdt_default_t1_chan;
			ch->sl.config = sl_default_t1_chan;
		} else {
			ch->option = lmi_default_t1_span;
			ch->sdl.config = sdl_default_t1_span;
			ch->sdt.config = sdt_default_t1_span;
			ch->sl.config = sl_default_t1_span;
		}
		break;
	case SDL_GTYPE_J1:
		if (chan != 0) {
			ch->option = lmi_default_j1_chan;
			ch->sdl.config = sdl_default_j1_chan;
			ch->sdt.config = sdt_default_j1_chan;
			ch->sl.config = sl_default_j1_chan;
		} else {
			ch->option = lmi_default_j1_span;
			ch->sdl.config = sdl_default_j1_span;
			ch->sdt.config = sdt_default_j1_span;
			ch->sl.config = sl_default_j1_span;
		}
		break;
	}
	/* initialize buffer queues */
	bufq_init(&ch->sdl.tb);
	bufq_init(&ch->sdt.tb);
	bufq_init(&ch->sl.rb);
	bufq_init(&ch->sl.tb);
	bufq_init(&ch->sl.rtb);
}

noinline __unlikely struct ch *
xp_alloc_ch(struct sp *sp, uint8_t chan)
{
	struct ch *ch;

	if ((ch = kmem_cache_alloc(xp_chan_cachep, GFP_ATOMIC))) {
		printd(("%s: allocated channel private structure\n", DRV_NAME));
		bzero(ch, sizeof(*ch));
		ch_get(ch);	/* first get */
		spin_lock_init(&ch->lock);	/* "ch-priv-lock" */
		/* create linkage */
		sp->chans[chan] = ch_get(ch);
		ch->sp = sp_get(sp);
		ss7_bufpool_reserve(&xp_bufpool, 2);
		printd(("%s: setting channel private structure defaults\n", DRV_NAME));
		xp_init_ch(sp, ch, chan);
	} else
		ptrace(("%s: ERROR: Could not allocate channel private structure\n", DRV_NAME));
	return (ch);
}

noinline __unlikely void
xp_free_ch(struct ch *ch)
{
	struct sp *sp;

	ensure(ch, return);

	spin_lock_bh(&ch->lock);
	{
#if 1
		xp_timer_stop(ch, tall);
#endif
		if ((sp = ch->sp)) {
			struct cd *cd;

			/* remove span linkage */
			if ((cd = sp->cd)) {
				spin_lock(&cd->lock);
				{
					int chan;
					uchar idle = (cd->config.ifgtype == SDL_GTYPE_T1) ? 0x7f : 0xff;
					uchar *base = (uchar *) cd->wbuf + span_to_byte(sp->span);

					for (chan = 0; chan < 32; chan++) {
						if (sp->chans[chan] == ch) {
							int boff, slot;

							ch_put(xchg(&sp->chans[chan], NULL));
							slot = (sp->config.ifgtype == SDL_GTYPE_E1)
							    ? xp_e1_chan_map[chan]
							    : xp_t1_chan_map[chan];
							for (boff = 0; boff < X400P_EBUFNO; boff++)
								*(base + (boff << 10) + (slot << 2)) = idle;
						}
					}
				}
				spin_unlock(&cd->lock);
			} else {
				int chan;

				for (chan = 0; chan < 32; chan++)
					if (sp->chans[chan] == ch)
						ch_put(xchg(&sp->chans[chan], NULL));
			}
			sp_put(xchg(&ch->sp, NULL));
			printd(("%s: unliked chan private structure from span\n", DRV_NAME));
		} else
			ptrace(("%s: ERROR: chans cannot exist without spans\n", DRV_NAME));
#if 0
		xp_free_timers(ch);
#endif
		if (ch->tx.msg && ch->tx.msg != ch->tx.cmp)
			freemsg(xchg(&ch->tx.msg, NULL));
		if (ch->tx.cmp)
			freemsg(xchg(&ch->tx.cmp, NULL));
		if (ch->rx.msg)
			freemsg(xchg(&ch->rx.msg, NULL));
		if (ch->rx.nxt)
			freemsg(xchg(&ch->rx.nxt, NULL));
		if (ch->rx.cmp)
			freemsg(xchg(&ch->rx.cmp, NULL));
		bufq_purge(&ch->sdl.tb);
		bufq_purge(&ch->sdt.tb);
		ss7_bufpool_release(&xp_bufpool, 2);
		bufq_purge(&ch->sl.rb);
		bufq_purge(&ch->sl.tb);
		bufq_purge(&ch->sl.rtb);
	}
	spin_unlock_bh(&ch->lock);
	assure(atomic_read(&ch->refcnt) == 1);
	ch_put(ch);		/* final put */
}

STATIC struct ch *
ch_get(struct ch *ch)
{
	if (ch)
		atomic_inc(&ch->refcnt);
	return (ch);
}

STATIC void
ch_put(struct ch *ch)
{
	if (atomic_dec_and_test(&ch->refcnt)) {
		printd(("%s: freed slot private structure\n", DRV_NAME));
		kmem_cache_free(xp_chan_cachep, ch);
	}
}

STATIC struct ch *
ch_find(uint ppa)
{
	struct sp *sp;
	uint chan;

	if ((sp = sp_find(ppa)) == NULL)
		return ERR_PTR(-ESRCH);
	chan = (ppa >> 0) & 0xff;
	switch (sp->config.ifgtype) {
	case SDL_GTYPE_E1:
		if (chan > 31)
			return ERR_PTR(-ESRCH);
		break;
	case SDL_GTYPE_T1:
	case SDL_GTYPE_J1:
		if (chan > 24)
			return ERR_PTR(-ESRCH);
		break;
	default:
		return ERR_PTR(-ESRCH);
	}
	return (sp->chans[chan]);
}

/*
 *  Span allocation and deallocation
 *  -------------------------------------------------------------------------
 */
noinline __unlikely void
xp_init_sp(struct cd *cd, struct sp *sp, uint8_t span)
{
	sp->iobase = cd->iobase + (span << 8);
	sp->span = span;
	sp->config = cd->config;
	sp->config.ifflags = 0;
	sp->config.ifalarms = 0;
	sp->config.ifrxlevel = 0;
}

noinline __unlikely struct sp *
xp_alloc_sp(struct cd *cd, uint8_t span)
{
	struct sp *sp;

	if ((sp = kmem_cache_alloc(xp_span_cachep, GFP_ATOMIC))) {
		printd(("%s: allocated span private structure\n", DRV_NAME));
		bzero(sp, sizeof(*sp));
		sp_get(sp);	/* first get */
		spin_lock_init(&sp->lock);	/* "sp-priv-lock" */
		/* create linkage */
		cd->spans[span] = sp_get(sp);
		sp->cd = cd_get(cd);
		/* fill out span structure */
		printd(("%s: linked span private structure\n", DRV_NAME));
		xp_init_sp(cd, sp, span);
		printd(("%s: set span private structure defaults\n", DRV_NAME));
	} else
		ptrace(("%s: ERROR: Could not allocate span private structure\n", DRV_NAME));
	return (sp);
}

/* Note: called with card interrupts disabled */
noinline __unlikely void
xp_free_sp(struct sp *sp)
{
	struct cd *cd;

	ensure(sp, return);
	if ((cd = sp->cd)) {
		int chan;

		/* remove card linkage */
		sp_put(xchg(&cd->spans[sp->span], NULL));
		cd_put(xchg(&sp->cd, NULL));
		sp->span = 0;
		printd(("%s: unlinked span private structure from card\n", DRV_NAME));
		/* remove any remaining chans */
		for (chan = 0; chan < 32; chan++) {
			struct ch *ch;

			if ((ch = sp->chans[chan]))
				xp_free_ch(ch);
		}
		printd(("%s: unlinked span private structure from slots\n", DRV_NAME));
	} else
		ptrace(("%s: ERROR: spans cannot exist without cards\n", DRV_NAME));
	assure(atomic_read(&sp->refcnt) == 1);
	sp_put(sp);		/* final put */
}

STATIC struct sp *
sp_get(struct sp *sp)
{
	if (sp)
		atomic_inc(&sp->refcnt);
	return (sp);
}

STATIC void
sp_put(struct sp *sp)
{
	if (atomic_dec_and_test(&sp->refcnt)) {
		printd(("%s: freed span private structure\n", DRV_NAME));
		kmem_cache_free(xp_span_cachep, sp);
	}
}

STATIC struct sp *
sp_find(uint ppa)
{
	struct cd *cd;
	struct sp *sp = NULL;
	uint span = (ppa >> 8) & 0x0f;

	if (span < X400_SPANS && (cd = cd_find(ppa)) != NULL)
		sp = cd->spans[span];
	return (sp);
}

/*
 *  Synchronization Group allocation and deallocation.
 *  -------------------------------------------------------------------------
 */
noinline __unlikely struct sg *
xp_alloc_sg(void)
{
	struct sg *sg;

	if ((sg = kmem_cache_alloc(xp_sync_cachep, GFP_ATOMIC))) {
		psw_t flags;

		bzero(sg, sizeof(*sg));
		sg_get(sg);	/* first get */
		spin_lock_init(&sg->lock);
		write_lock_irqsave(&x400p_lock, flags);
		if (x400p_groups >= X400_SYNCS) {
			kmem_cache_free(xp_sync_cachep, sg);
			write_unlock_irqrestore(&x400p_lock, flags);
			return (NULL);
		}
		sg->group = x400p_groups++;
		x400p_syncs[sg->group] = sg_get(sg);
		write_unlock_irqrestore(&x400p_lock, flags);
		printd(("%s: linked sync private structure\n", DRV_NAME));
	} else
		ptrace(("%s: ERROR: Could not allocate sync private structure\n", DRV_NAME));
	return (sg);
}

noinline __unlikely void
xp_free_sg(struct sg *sg)
{
	psw_t flags;

	ensure(sg, return);
	write_lock_irqsave(&x400p_lock, flags);
	x400p_syncs[sg->group] = NULL;
	write_unlock_irqrestore(&x400p_lock, flags);
	sg_put(sg);
	printd(("%s: unlinked sync private structure\n", DRV_NAME));
	assure(atomic_read(&sg->refcnt) == 1);
	sg_put(sg);		/* final put */
}

STATIC struct sg *
sg_get(struct sg *sg)
{
	if (sg)
		atomic_inc(&sg->refcnt);
	return (sg);
}

STATIC void
sg_put(struct sg *sg)
{
	if (atomic_dec_and_test(&sg->refcnt)) {
		kmem_cache_free(xp_sync_cachep, sg);
		printd(("%s: freed sync private structure\n", DRV_NAME));
	}
}

STATIC struct sg *
sg_find(uint group)
{
	struct sg *sg = NULL;

	if (group < X400_SYNCS)
		sg = x400p_syncs[group];
	return (sg);
}

/*
 *  Card allocation and deallocation
 *  -------------------------------------------------------------------------
 */
noinline __unlikely struct cd *
xp_alloc_cd(void)
{
	struct cd *cd;

	if ((cd = kmem_cache_alloc(xp_card_cachep, GFP_ATOMIC))) {
		uint32_t *wbuf;
		uint32_t *rbuf;
		psw_t flags;

		printd(("%s: allocated card private structure\n", DRV_NAME));
		if (!(wbuf = kmem_cache_alloc(xp_xbuf_cachep, GFP_ATOMIC))) {
			ptrace(("%s: could not allocate write buffer\n", DRV_NAME));
			kmem_cache_free(xp_card_cachep, cd);
			return (NULL);
		}
		if (!(rbuf = kmem_cache_alloc(xp_xbuf_cachep, GFP_ATOMIC))) {
			ptrace(("%s: could not allocate read buffer\n", DRV_NAME));
			kmem_cache_free(xp_xbuf_cachep, wbuf);
			kmem_cache_free(xp_card_cachep, cd);
			return (NULL);
		}
		bzero(cd, sizeof(*cd));
		cd_get(cd);	/* first get */
		spin_lock_init(&cd->lock);	/* "cd-priv-lock" */
		write_lock_irqsave(&x400p_lock, flags);
		if (x400p_boards >= X400_CARDS) {
			kmem_cache_free(xp_xbuf_cachep, rbuf);
			kmem_cache_free(xp_xbuf_cachep, wbuf);
			kmem_cache_free(xp_card_cachep, cd);
			write_unlock_irqrestore(&x400p_lock, flags);
			return (NULL);
		}
		cd->card = x400p_boards++;
		x400p_cards[cd->card] = cd_get(cd);
		write_unlock_irqrestore(&x400p_lock, flags);
		bzero(wbuf, X400P_EBUFNO * 1024);
		bzero(rbuf, X400P_EBUFNO * 1024);
		cd->wbuf = wbuf;
		cd->rbuf = rbuf;
		tasklet_init(&cd->tasklet, &xp_card_tasklet, (unsigned long) cd);
		printd(("%s: linked card private structure\n", DRV_NAME));
	} else
		ptrace(("%s: ERROR: Could not allocate card private structure\n", DRV_NAME));
	return (cd);
}

/* Note: called with card interrupts disabled and pci resources deallocated */
noinline __unlikely void
xp_free_cd(struct cd *cd)
{
	psw_t flags;

	ensure(cd, return);
	spin_lock_irqsave(&cd->lock, flags);
	{
		int span;
		struct sg *sg;

		if (cd->tasklet.func)
			tasklet_kill(&cd->tasklet);
		if (cd->rbuf)
			kmem_cache_free(xp_xbuf_cachep, (uint32_t *) xchg(&cd->rbuf, NULL));
		if (cd->wbuf)
			kmem_cache_free(xp_xbuf_cachep, (uint32_t *) xchg(&cd->wbuf, NULL));
		/* remove any remaining spans */
		for (span = 0; span < X400_SPANS; span++) {
			struct sp *sp;

			if ((sp = cd->spans[span]))
				xp_free_sp(sp);
		}
		/* unlink from sync list */
		if ((sg = cd->sg.sg) != NULL) {
			spin_lock(&sg->lock);
			if ((*(cd->sg.prev) = cd->sg.next))
				cd->sg.next->sg.prev = cd->sg.prev;
			cd->sg.next = NULL;
			cd->sg.prev = &cd->sg.next;
			sg->members--;
			cd->sg.sg = NULL;
			spin_unlock(&sg->lock);
			sg_put(sg);
		}
		/* unlink from board array */
		x400p_cards[cd->card] = NULL;
		cd_put(cd);
		printd(("%s: unlinked card private structure\n", DRV_NAME));
	}
	spin_unlock_irqrestore(&cd->lock, flags);
	assure(atomic_read(&cd->refcnt) == 1);
	cd_put(cd);		/* final put */
}

STATIC struct cd *
cd_get(struct cd *cd)
{
	if (cd)
		atomic_inc(&cd->refcnt);
	return (cd);
}

STATIC void
cd_put(struct cd *cd)
{
	if (atomic_dec_and_test(&cd->refcnt)) {
		kmem_cache_free(xp_card_cachep, cd);
		printd(("%s: freed card private structure\n", DRV_NAME));
	}
}

STATIC struct cd *
cd_find(uint ppa)
{
	struct cd *cd = NULL;
	uint card = (ppa >> 12) & 0x0f;

	if (card < X400_CARDS)
		cd = x400p_cards[card];
	return (cd);
}

/*
 *  =========================================================================
 *
 *  PCI Initialization
 *
 *  =========================================================================
 */

/** xp_remove: - X400P-SS7 Remove
  *
  * These cards do not support hotplug, so removes only occur after all the channels have been
  * closed, so we only have to stop interrupts and deallocate board-level resources.  Nevertheless,
  * if we get a hot removal of a card, we must be prepared to deallocate the span structures.
  */
STATIC void
xp_remove(struct pci_dev *dev)
{
	struct cd *cd;

	if (!(cd = pci_get_drvdata(dev)))
		goto disable;
	if (cd->plx) {
		cd->plx[INTCSR] = 0;	/* disable interrupts */
	}
	if (cd->xlb) {
		cd->xlb[SYNREG] = cd->synreg = SYNCSELF;
		cd->xlb[CTLREG] = cd->ctlreg = 0;
		cd->xlb[LEDREG] = cd->ledreg = 0;
		cd->xlb[TSTREG] = cd->tstreg = 0;
		cd->xlb[CTLREG1] = 0;
	}
	if (cd->irq) {
		free_irq(cd->irq, cd);
		printd(("%s: freed irq\n", DRV_NAME));
	}
	if (cd->xlb_region) {
		release_mem_region(cd->xlb_region, cd->xlb_length);
		printd(("%s: released xlb region %lx length %ld\n", DRV_NAME, cd->xlb_region,
			cd->xlb_length));
	}
	if (cd->xll_region) {
		release_mem_region(cd->xll_region, cd->xll_length);
		printd(("%s: released xll region %lx length %ld\n", DRV_NAME, cd->xll_region,
			cd->xll_length));
	}
	if (cd->plx_region) {
		release_mem_region(cd->plx_region, cd->plx_length);
		printd(("%s: released plx region %lx length %ld\n", DRV_NAME, cd->plx_region,
			cd->plx_length));
	}
	if (cd->xlb) {
		iounmap((void *) cd->xlb);
		printd(("%s: unmapped xlb memory at %p\n", DRV_NAME, cd->xlb));
	}
	if (cd->xll) {
		iounmap((void *) cd->xll);
		printd(("%s: unmapped xll memory at %p\n", DRV_NAME, cd->xll));
	}
	if (cd->plx) {
		iounmap((void *) cd->plx);
		printd(("%s: unmapped plx memory at %p\n", DRV_NAME, cd->plx));
	}
	xp_free_cd(cd);
      disable:
	pci_disable_device(dev);
}

#ifdef X400P_DOWNLOAD_FIRMWARE
/** xp_download_fw: - download Xilinx firmware to card.
  * @cd: card structure
  * @board: board number (not used)
  *
  * This procedure downloads firmware to the card.  It is usually not necessary to download firmware
  * to the card.  Only a very ancient and experimental Tormenta card would not already have the
  * proper firmware loaded.
  */
STATIC int __devinit
xp_download_fw(struct cd *cd, enum xp_board board)
{
	unsigned int byte;
	unsigned char *f;
	size_t flen;
	volatile unsigned long *data;
	unsigned long timeout;

#if 0
	/* prints out PLX registers for debugging */
	{
		char buf[64] = { 0, };
		volatile unsigned char *c;
		int i, offs = 0;

		for (c = (volatile unsigned char *) cd->plx, i = 0; i < cd->plx_length; i++, c++) {
			if (i && !(i & 0x0f)) {
				__printd(("%s\n", buf));
				buf[0] = '\0';
				offs = 0;
			}
			offs += snprintf(buf + offs, sizeof(buf) - offs, "%02x ", *c);
		}
		if (offs) {
			__printd(("%s\n", buf));
		}
	}
#endif

	f = (unsigned char *) v401pfw;
	flen = sizeof(v401pfw);

	data = (volatile unsigned long *) &cd->plx[GPIOC];
	*data |= GPIO_WRITE;
	*data &= ~GPIO_PROGRAM;
	while (*data & (GPIO_INIT | GPIO_DONE)) ;
	printd(("%s: Xilinx Firmware Load: Init and done are low\n", DRV_NAME));
	*data |= GPIO_PROGRAM;
	while (!(*data & GPIO_INIT)) ;
	printd(("%s: Xilinx Firmware Load: Init is high\n", DRV_NAME));
	*data &= ~GPIO_WRITE;
	printd(("%s: Xilinx Firmware Load: Loading\n", DRV_NAME));
	for (byte = 0; byte < flen; byte++) {
		*cd->xlb = *f++;
		if (*data & GPIO_DONE)
			break;
		if (!(*data & GPIO_INIT))
			break;
	}
	if (!(*data & GPIO_INIT)) {
		printd(("%s: ERROR: Xilinx Firmware Load: Failed\n", DRV_NAME));
		return (-EIO);
	}
	printd(("%s: Xilinx Firmware Load: Loaded %d bytes\n", DRV_NAME, byte));
	timeout = (volatile unsigned long) jiffies + 20 * HZ / 1000;
	while ((volatile unsigned long) jiffies < timeout) ;
	*data |= GPIO_WRITE;
	printd(("%s: Xilinx Firmware Load: Done\n", DRV_NAME));
	timeout = (volatile unsigned long) jiffies + 20 * HZ / 1000;
	while ((volatile unsigned long) jiffies < timeout) ;
	if (!(*data & GPIO_INIT)) {
		cmn_err(CE_WARN, "%s: ERROR: Xilinx Firmware Load: Failed", DRV_NAME);
		return (-EIO);
	}
	if (!(*data & GPIO_DONE)) {
		cmn_err(CE_WARN, "%s: ERROR: Xilinx Firmware Load: Failed", DRV_NAME);
		return (-EIO);
	}
	cmn_err(CE_NOTE, "%s: Xilinx Firmware Load: Successful", DRV_NAME);
	return (0);
}
#endif

#ifdef IRQF_DISABLED
#undef SA_INTERRUPT
#define SA_INTERRUPT IRQF_DISABLED
#endif

#ifdef IRQF_SHARED
#undef SA_SHIRQ
#define SA_SHIRQ IRQF_SHARED
#endif

#ifndef HAVE_KFUNC_PCI_IS_PCIE
#define pci_is_pcie(dev) (dev->is_pcie)
#endif

/** xp_probe: - X400P-SS7 Probe
  * @dev: PCI device
  * @id: PCI device id
  *
  * Probes will be called for all PCI devices which match our criteria at pci init time (module
  * load).  Successful return from the probe function will have the device configured for operation.
  */
STATIC int __devinit
xp_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
	int board;
	struct cd *cd;
	const char *name;

	if (!dev || !id) {
		cmn_err(CE_WARN, "%s: ERROR: Device or id is null!", DRV_NAME);
		return (-ENXIO);
	}
	board = id->driver_data;
	name = xp_board_info[board].name;

	if (!(xp_board_info[board].hw_flags & XPF_SUPPORTED)) {
		cmn_err(CE_WARN, "%s: ERROR: Driver does not support %s card.", DRV_NAME, name);
		return (-ENXIO);
	}
	if (pci_enable_device(dev)) {
		cmn_err(CE_WARN, "%s: ERROR: Could not enable %s pci card", DRV_NAME, name);
		return (-ENODEV);
	}
	printd(("%s: enabled %s pci card type %ld\n", DRV_NAME, name, id->driver_data));
	if (dev->irq < 1) {
		cmn_err(CE_WARN, "%s: ERROR: No IRQ allocated for %s card.", DRV_NAME, name);
		pci_disable_device(dev);
		return (-ENXIO);
	}
	printd(("%s: card %s allocated IRQ %d\n", DRV_NAME, name, dev->irq));
	if (!(cd = xp_alloc_cd())) {
		pci_disable_device(dev);
		return (-ENOMEM);
	}
	pci_set_drvdata(dev, cd);
	if ((pci_resource_flags(dev, 0) & IORESOURCE_IO)
	    || !(cd->plx_region = pci_resource_start(dev, 0))
	    || !(cd->plx_length = pci_resource_len(dev, 0))
	    || !(cd->plx = ioremap(cd->plx_region, cd->plx_length))) {
		cmn_err(CE_WARN, "%s: ERROR: Invalid PLX 9030 base resource", DRV_NAME);
		goto error_remove;
	}
	printd(("%s: plx region %ld bytes at %lx, remapped %p\n", DRV_NAME, cd->plx_length,
		cd->plx_region, cd->plx));
	if ((pci_resource_flags(dev, 2) & IORESOURCE_IO)
	    || !(cd->xll_region = pci_resource_start(dev, 2))
	    || !(cd->xll_length = pci_resource_len(dev, 2))
	    || !(cd->xll = ioremap(cd->xll_region, cd->xll_length))) {
		cmn_err(CE_WARN, "%s: ERROR: Invalid Xilinx 32-bit base resource", DRV_NAME);
		goto error_remove;
	}
	printd(("%s: xll region %ld bytes at %lx, remapped %p\n", DRV_NAME, cd->xll_length,
		cd->xll_region, cd->xll));
	if ((pci_resource_flags(dev, 3) & IORESOURCE_IO)
	    || !(cd->xlb_region = pci_resource_start(dev, 3))
	    || !(cd->xlb_length = pci_resource_len(dev, 3))
	    || !(cd->xlb = ioremap(cd->xlb_region, cd->xlb_length))) {
		cmn_err(CE_WARN, "%s: ERROR: Invalid Xilinx 8-bit base resource", DRV_NAME);
		goto error_remove;
	}
	printd(("%s: xlb region %ld bytes at %lx, remapped %p\n", DRV_NAME, cd->xlb_length,
		cd->xlb_region, cd->xlb));
	cd->iobase = (ulong) cd->xlb;
	cd->config.ifname = xp_board_info[id->driver_data].name;
	if (!request_mem_region(cd->plx_region, cd->plx_length, cd->config.ifname)) {
		cmn_err(CE_WARN, "%s: ERROR: Unable to reserve PLX memory", DRV_NAME);
		goto error_remove;
	}
	printd(("%s: plx region %lx reserved %ld bytes\n", DRV_NAME, cd->plx_region,
		cd->plx_length));
	if (!request_mem_region(cd->xll_region, cd->xll_length, cd->config.ifname)) {
		cmn_err(CE_WARN, "%s: ERROR: Unable to reserve Xilinx 32-bit memory", DRV_NAME);
		goto error_remove;
	}
	printd(("%s: xll region %lx reserved %ld bytes\n", DRV_NAME, cd->xll_region,
		cd->xll_length));
	if (!request_mem_region(cd->xlb_region, cd->xlb_length, cd->config.ifname)) {
		cmn_err(CE_WARN, "%s: ERROR: Unable to reserve Xilinx 8-bit memory", DRV_NAME);
		goto error_remove;
	}
	printd(("%s: xlb region %lx reserved %ld bytes\n", DRV_NAME, cd->xlb_region,
		cd->xlb_length));
	cmn_err(CE_NOTE, "%s: card detected %s at 0x%lx/0x%lx irq %d", DRV_NAME, cd->config.ifname,
		cd->xll_region, cd->xlb_region, dev->irq);
#ifdef X400P_DOWNLOAD_FIRMWARE
	if (xp_download_fw(cd, board) != 0)
		goto error_remove;
#endif
	cd->plx[INTCSR] = 0;	/* disable interrupts */

	/* all boards have these registers regardless of framer */

	cd->xlb[SYNREG] = cd->synreg = SYNCSELF;	/* default autosync */
	cd->xlb[CTLREG] = cd->ctlreg = 0;	/* interrupts disabled */
	cd->xlb[LEDREG] = cd->ledreg = 0;	/* turn off leds */
	cd->xlb[TSTREG] = cd->tstreg = 0;	/* do not drive TEST2 pin */
	cd->xlb[CTLREG1] = 0;	/* Rev. A mode */

	/* Note: only check the device id of the first framer of 4. */

	cd->devrev = cd->xlb[0x0f];	/* Device id - same place for all Dallas chips */
	cd->device = (cd->devrev & XP_DEV_IDMASK) >> XP_DEV_SHIFT;
	cd->devrev &= XP_DEV_REVMASK;

	if (!(xp_device_info[cd->device].hw_flags & XPF_SUPPORTED)) {
		cmn_err(CE_WARN, "%s: Usupported framer device %s", DRV_NAME,
			xp_device_info[cd->device].name);
		goto error_remove;
	}
	switch (cd->device) {
	case XP_DEV_DS2154:
	case XP_DEV_DS21354:
	case XP_DEV_DS21554:
		/* ISR depends only on device type. */
		cd->isr = &xp_e1_interrupt;
		/* The only way to tell whether E1 from T1 boards; i.e., by chipset. */
		switch (board) {
		case V400P:
			/* This will also handle any older ATCOM cards that also used the '4000'H PCI id yet
			   had these chipsets. */
			cd->board = V400PE;
			break;
		case X400P:
			cd->board = E400P;
			break;
		case X400PSS7:
			cd->board = E400PSS7;
			break;
		default:
			cd->board = -1;
			break;
		}
		break;
	case XP_DEV_DS2152:
	case XP_DEV_DS21352:
	case XP_DEV_DS21552:
		/* ISR depends only on device type. */
		cd->isr = &xp_t1_interrupt;
		/* The only way to tell whether E1 from T1 boards; i.e., by chipset. */
		switch (board) {
		case V400P:
			/* This will also handle any older ATCOM cards that also used the '4000'H PCI id yet
			   had these chipsets. */
			cd->board = V400PT;
			break;
		case X400P:
			cd->board = T400P;
			break;
		case X400PSS7:
			cd->board = T400PSS7;
			break;
		default:
			cd->board = -1;
			break;
		}
		break;
	case XP_DEV_DS2155:
	case XP_DEV_DS21455:
	case XP_DEV_DS21458:
	case XP_DEV_DS2156:
		/* ISR depends only on device type. */
		cd->isr = &xp_x1_interrupt;
		switch (board) {
		case V400P:
			/* This is the only way to distinguish a ATCOM TE400P card from an old tor3 Varion
			   card: i.e., by chipset. */
			cd->board = A400P;
			break;
		case V401PE:
			cd->board = V401PE;
			break;
		case A400PE:
			cd->board = A400PE;
			break;
		case V401PT:
			cd->board = V401PT;
			break;
		case A400PT:
			cd->board = A400PT;
			break;
		case CP100:
			cd->board = pci_is_pcie(dev) ? CP100E : CP100P;
			break;
		case CP200:
			cd->board = pci_is_pcie(dev) ? CP200E : CP200P;
			break;
		case CP400:
			cd->board = pci_is_pcie(dev) ? CP400E : CP400P;
			break;
		default:
			cd->board = -1;
			break;
		}
		break;
	}
	if (cd->board == -1) {
		cmn_err(CE_WARN, "%s: Device %s not supported for card %s", DRV_NAME,
			xp_device_info[cd->device].name, xp_board_info[board].name);
		goto error_remove;
	}
	cd->ports = xp_board_info[cd->board].ports;
	cd->hw_flags = xp_board_info[cd->board].hw_flags;
	cd->hw_flags |= xp_device_info[cd->device].hw_flags;

	cmn_err(CE_NOTE, "%s: %s (%s Rev. %c)", DRV_NAME, xp_board_info[cd->board].name,
		xp_device_info[cd->device].name, (char) (cd->devrev + 65));

	if (request_irq(dev->irq, cd->isr, SA_INTERRUPT | SA_SHIRQ, DRV_NAME, cd)) {
		cmn_err(CE_WARN, "%s: ERROR: Unable to request IRQ %d", DRV_NAME, dev->irq);
		goto error_remove;
	}
	cd->irq = dev->irq;
	cd->bus = dev->bus->number;
	cd->slot = PCI_SLOT(dev->devfn);
	printd(("%s: acquired IRQ %ld for %s card\n", DRV_NAME, cd->irq,
		xp_board_info[cd->board].name));

	if (xp_card_config(cd) != 0)
		goto error_remove;

	cd->plx[INTCSR] = PLX_INTENA;	/* enable interrupts */
	return (0);
      error_remove:
	xp_remove(dev);
	return (-ENODEV);
}

#if 0
#ifdef CONFIG_PM
#ifndef HAVE_KTYPE_PM_MESSAGE_T
typedef u32 pm_message_t;
#endif

/** xp_suspend: - X400P-SS7 Suspend
  * @pdev: PCI device
  * @state: suspend state
  */
STATIC int
xp_suspend(struct pci_dev *pdev, pm_message_t state)
{
	fixme(("Write a suspend routine.\n"));
	return 0;
}

/** xp_resume: - X400P-SS7 Resume
  * @pdev: PCI device
  */
STATIC int
xp_resume(struct pci_dev *pdev)
{
	fixme(("Write a resume routine.\n"));
	return 0;
}
#endif				/* CONFIG_PM */
#endif

STATIC struct pci_driver xp_driver = {
	.name = DRV_NAME,
	.probe = xp_probe,
	.remove = __devexit_p(xp_remove),
	.id_table = xp_pci_tbl,
#if 0
#ifdef CONFIG_PM
	.suspend = xp_suspend,
	.resume = xp_resume,
#endif				/* CONFIG_PM */
#endif
};

/** xp_pci_init: - X400P-SS7 PCI Init
  *
  * Here we need to scan for all available boards, configure the board-level structures, and then
  * release all system resources.  The purpose of this is to identify the boards in the system at
  * module startup or Linux Fast-STREAMS initialization.
  *
  * Later, when a stream is opened for any span, the span is configured, the drivers will be enabled
  * and all channels in the span will have their power-on sequence completed and each channel will
  * idle SIOS.  Closing channels will result in the transmitters resuming idle SIOS operation.
  */
noinline __devinit int
xp_pci_init(void)
{
#ifdef HAVE_KFUNC_PCI_MODULE_INIT
	return pci_module_init(&xp_driver);
#else				/* HAVE_KFUNC_PCI_MODULE_INIT */
	return pci_register_driver(&xp_driver);
#endif				/* HAVE_KFUNC_PCI_MODULE_INIT */
}

/** xp_pci_cleanup: - X400P-SS7 PCI Cleanup
  *
  * Because this is a style 2 driver, if there are any boards that are atill configured, we need to
  * stop the boards and deallocate the board-level resources and structures.
  */
noinline int
xp_pci_cleanup(void)
{
	pci_unregister_driver(&xp_driver);
	return (0);
}

/*
 *  =========================================================================
 *
 *  Registration and initialization
 *
 *  =========================================================================
 */
#ifdef LINUX
/*
 *  Linux Registration
 *  -------------------------------------------------------------------------
 */

unsigned short modid = DRV_ID;

#ifndef module_param
MODULE_PARM(modid, "h");
#else
module_param(modid, ushort, 0444);
#endif
MODULE_PARM_DESC(modid, "Module ID for the SL-X400P driver. (0 for allocation.)");

major_t major = CMAJOR_0;

#ifndef module_param
MODULE_PARM(major, "h");
#else
module_param(major, uint, 0444);
#endif
MODULE_PARM_DESC(major, "Device number for the SL-X400P driver. (0 for allocation.)");

/*
 *  Linux Fast-STREAMS Registration
 *  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */
STATIC struct cdevsw xp_cdev = {
	.d_name = DRV_NAME,
	.d_str = &sl_x400pinfo,
	.d_flag = D_MP,
	.d_fop = NULL,
	.d_mode = S_IFCHR,
	.d_kmod = THIS_MODULE,
};

noinline __devinit int
xp_register_strdev(major_t major)
{
	int err;

	if ((err = register_strdev(&xp_cdev, major)) < 0)
		return (err);
	return (0);
}

noinline int
xp_unregister_strdev(major_t major)
{
	int err;

	if ((err = unregister_strdev(&xp_cdev, major)) < 0)
		return (err);
	return (0);
}

MODULE_STATIC void
sl_x400pterminate(void)
{
	int err, mindex;

	if (xp_majors[0]) {
		unregister_bpf(&xp_cdev);
		unregister_nit(&xp_cdev);
	}
	for (mindex = CMAJORS - 1; mindex >= 0; mindex--) {
		if (xp_majors[mindex]) {
			if ((err = xp_unregister_strdev(xp_majors[mindex])))
				cmn_err(CE_PANIC, "%s: cannot unregister major %d", DRV_NAME,
					xp_majors[mindex]);
			if (mindex)
				xp_majors[mindex] = 0;
		}
	}
	ss7_bufpool_term(&xp_bufpool);
	if ((err = xp_free_tables()))
		cmn_err(CE_WARN, "%s: could not free tables", DRV_NAME);
	if ((err = xp_pci_cleanup()))
		cmn_err(CE_WARN, "%s: could not cleanup pci device", DRV_NAME);
	if ((err = xp_term_caches()))
		cmn_err(CE_WARN, "%s: could not terminate caches", DRV_NAME);
	return;
}

MODULE_STATIC int __init
sl_x400pinit(void)
{
	int err, mindex = 0;

	(void) &xp_free_ch;
	(void) &xp_alloc_sg;
	(void) &xp_free_sg;
	(void) &sg_find;
	(void) &xp_span_config;

	cmn_err(CE_NOTE, DRV_BANNER);	/* console splash */
	if ((err = xp_init_caches())) {
		cmn_err(CE_WARN, "%s: could not init caches, err = %d", DRV_NAME, err);
		sl_x400pterminate();
		return (err);
	}
	if ((err = xp_pci_init())) {
		cmn_err(CE_WARN, "%s: no PCI devices found, err = %d", DRV_NAME, err);
		sl_x400pterminate();
		return (err);
	}
	if ((err = xp_init_tables())) {
		cmn_err(CE_WARN, "%s: could not allocate tables, err = %d", DRV_NAME, err);
		sl_x400pterminate();
		return (err);
	}
	ss7_bufpool_init(&xp_bufpool);
	for (mindex = 0; mindex < CMAJORS; mindex++) {
		if ((err = xp_register_strdev(xp_majors[mindex])) < 0) {
			if (mindex) {
				cmn_err(CE_WARN, "%s: could not register major %d", DRV_NAME,
					xp_majors[mindex]);
				continue;
			} else {
				cmn_err(CE_WARN, "%s: could not register driver, err = %d",
					DRV_NAME, err);
				sl_x400pterminate();
				return (err);
			}
		}
		if (xp_majors[mindex] == 0)
			xp_majors[mindex] = err;
#if 0
		LIS_DEVFLAGS(xp_majors[mindex]) |= LIS_MODFLG_CLONE;
#endif
		if (major == 0)
			major = xp_majors[0];
	}
	if ((err = register_nit(&xp_cdev)) < 0) {
		cmn_err(CE_WARN, "%s: Could not register with nit driver, err = %d", DRV_NAME, err);
		sl_x400pterminate();
		return (err);
	}
	if ((err = register_bpf(&xp_cdev)) < 0) {
		cmn_err(CE_WARN, "%s: Could not register with bpf driver, err = %d", DRV_NAME, err);
		sl_x400pterminate();
		return (err);
	}
	return (0);
}

/*
 *  Linux Kernel Module Initialization
 *  -------------------------------------------------------------------------
 */
module_init(sl_x400pinit);
module_exit(sl_x400pterminate);

#endif				/* LINUX */
